@rapidmx/web-client 0.20.0 → 0.22.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 (906) hide show
  1. package/README.md +415 -381
  2. package/apps/admin/_layout.tsx +2 -2
  3. package/apps/admin/audit-log/index.tsx +4 -4
  4. package/apps/admin/branding/index.tsx +39 -39
  5. package/apps/admin/data-requests/index.tsx +482 -482
  6. package/apps/admin/distribution-lists/[uid].tsx +3 -3
  7. package/apps/admin/distribution-lists/index.tsx +4 -4
  8. package/apps/admin/distribution-lists/new/index.tsx +5 -5
  9. package/apps/admin/domains/[uid].tsx +166 -166
  10. package/apps/admin/domains/index.tsx +4 -4
  11. package/apps/admin/domains/new/index.tsx +5 -5
  12. package/apps/admin/encryption-policy/index.tsx +1 -1
  13. package/apps/admin/escrow-scopes/[uid].tsx +350 -350
  14. package/apps/admin/escrow-scopes/index.tsx +4 -4
  15. package/apps/admin/escrow-scopes/new/index.tsx +5 -5
  16. package/apps/admin/index.tsx +129 -129
  17. package/apps/admin/ingest-queue/index.tsx +4 -4
  18. package/apps/admin/mailbox-policy/index.tsx +1 -1
  19. package/apps/admin/mailboxes/[uid].tsx +271 -271
  20. package/apps/admin/mailboxes/new/index.tsx +30 -30
  21. package/apps/admin/plugins/index.tsx +15 -15
  22. package/apps/admin/quarantine/index.tsx +5 -5
  23. package/apps/admin/retention-policy/index.tsx +39 -39
  24. package/apps/admin/signing-certificates/index.tsx +343 -343
  25. package/apps/admin/transport-rules/[uid].tsx +5 -5
  26. package/apps/admin/transport-rules/_transportRuleConfig.tsx +3 -3
  27. package/apps/admin/transport-rules/index.tsx +4 -4
  28. package/apps/admin/transport-rules/new/index.tsx +5 -5
  29. package/apps/escrow/_layout.tsx +1 -1
  30. package/apps/escrow/audit-log/index.tsx +196 -196
  31. package/apps/escrow/index.tsx +4 -4
  32. package/apps/escrow/matters/[uid].tsx +621 -621
  33. package/apps/escrow/matters/new/index.tsx +6 -6
  34. package/apps/shared/appearance/AppearanceForm.tsx +3 -3
  35. package/apps/shared/appearance/AppearanceHead.tsx +1 -1
  36. package/apps/shared/appearance/AppearanceProvider.tsx +3 -3
  37. package/apps/shared/appearance/appearanceCache.ts +1 -1
  38. package/apps/shared/appearance/appearanceContext.ts +1 -1
  39. package/apps/shared/appearance/theme.ts +1 -1
  40. package/apps/shared/auth/adminAccess.ts +99 -99
  41. package/apps/shared/calendar/bookingPlugin.ts +1 -1
  42. package/apps/shared/calendar/calendarLiveUpdates.ts +1 -1
  43. package/apps/shared/calendar/calendarReminders.ts +1 -1
  44. package/apps/shared/calendar/useCalendarReminders.ts +1 -1
  45. package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +1 -1
  46. package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +2 -2
  47. package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +2 -2
  48. package/apps/shared/components/admin/diagnostics/PackagesTable.tsx +1 -1
  49. package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
  50. package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +2 -2
  51. package/apps/shared/components/admin/diagnostics/VersionsPanel.tsx +1 -1
  52. package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +1 -1
  53. package/apps/shared/components/admin/diagnostics/format.ts +1 -1
  54. package/apps/shared/components/admin/diagnostics/logClient.ts +1 -1
  55. package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
  56. package/apps/shared/components/admin/elevation.ts +1 -1
  57. package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +3 -3
  58. package/apps/shared/components/admin/layout/AdminShell.tsx +384 -384
  59. package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +238 -238
  60. package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +154 -152
  61. package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +194 -194
  62. package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +1 -1
  63. package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +19 -14
  64. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +5 -5
  65. package/apps/shared/components/admin/settings/BrandingForm.tsx +423 -423
  66. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +5 -5
  67. package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +119 -119
  68. package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +2 -2
  69. package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +32 -27
  70. package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +198 -198
  71. package/apps/shared/components/admin/settings/PluginsManager.tsx +2093 -2093
  72. package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +182 -182
  73. package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +288 -288
  74. package/apps/shared/components/admin/setup/SetupWizard.tsx +450 -446
  75. package/apps/shared/components/admin/signOut.ts +1 -1
  76. package/apps/shared/components/admin/usePagedList.tsx +129 -129
  77. package/apps/shared/components/calendar/AppointmentQuickForm.tsx +3 -3
  78. package/apps/shared/components/calendar/CalendarListSidebar.tsx +3 -3
  79. package/apps/shared/components/calendar/DescriptionEditor.tsx +1 -1
  80. package/apps/shared/components/calendar/EventDescriptionView.tsx +1 -1
  81. package/apps/shared/components/calendar/EventDetails.tsx +16 -14
  82. package/apps/shared/components/calendar/EventEditor.tsx +54 -39
  83. package/apps/shared/components/calendar/EventExpandedForm.tsx +3 -3
  84. package/apps/shared/components/calendar/EventFormParts.tsx +2 -2
  85. package/apps/shared/components/calendar/EventModal.tsx +206 -206
  86. package/apps/shared/components/calendar/EventQuickForm.tsx +3 -3
  87. package/apps/shared/components/calendar/EventShell.tsx +1 -1
  88. package/apps/shared/components/calendar/FindATime.tsx +6 -4
  89. package/apps/shared/components/calendar/GuestInput.tsx +2 -2
  90. package/apps/shared/components/calendar/MonthView.tsx +185 -185
  91. package/apps/shared/components/calendar/QuickCreateFaces.tsx +1 -1
  92. package/apps/shared/components/calendar/QuickFaceFrame.tsx +1 -1
  93. package/apps/shared/components/calendar/RecurrenceEditor.tsx +227 -227
  94. package/apps/shared/components/calendar/RequestChangeForm.tsx +8 -6
  95. package/apps/shared/components/calendar/ResourcePicker.tsx +7 -5
  96. package/apps/shared/components/calendar/SplitDayView.tsx +144 -144
  97. package/apps/shared/components/calendar/TaskQuickForm.tsx +23 -18
  98. package/apps/shared/components/calendar/TimeGridView.tsx +246 -246
  99. package/apps/shared/components/calendar/allDay.ts +124 -124
  100. package/apps/shared/components/calendar/eventDialogFields.ts +3 -3
  101. package/apps/shared/components/calendar/eventForm.ts +3 -3
  102. package/apps/shared/components/calendar/eventFormat.ts +3 -3
  103. package/apps/shared/components/calendar/findTimeGrid.ts +2 -2
  104. package/apps/shared/components/calendar/layout/CalendarShell.tsx +11 -9
  105. package/apps/shared/components/contacts/ContactDetailPane.tsx +4 -4
  106. package/apps/shared/components/contacts/ContactForm.tsx +393 -383
  107. package/apps/shared/components/contacts/ContactsSidebar.tsx +7 -5
  108. package/apps/shared/components/contacts/ContactsToolbar.tsx +103 -103
  109. package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -5
  110. package/apps/shared/components/contacts/contactKeys.ts +3 -3
  111. package/apps/shared/components/contacts/layout/ContactsShell.tsx +9 -7
  112. package/apps/shared/components/escrow/layout/EscrowShell.tsx +155 -155
  113. package/apps/shared/components/forms/StringListField.tsx +1 -1
  114. package/apps/shared/components/layout/AppShell.tsx +465 -461
  115. package/apps/shared/components/layout/BrandingChrome.tsx +1 -1
  116. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -398
  117. package/apps/shared/components/layout/MailboxProvisioning.tsx +170 -168
  118. package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +9 -9
  119. package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -315
  120. package/apps/shared/components/layout/ThemeSwitch.tsx +84 -84
  121. package/apps/shared/components/layout/UnlockPromptProvider.tsx +6 -6
  122. package/apps/shared/components/layout/UserMenu.tsx +441 -439
  123. package/apps/shared/components/mail/ConversationList.tsx +334 -332
  124. package/apps/shared/components/mail/ConversationThreadPane.tsx +624 -613
  125. package/apps/shared/components/mail/InviteCard.tsx +11 -9
  126. package/apps/shared/components/mail/LazyReadingPane.tsx +2 -2
  127. package/apps/shared/components/mail/MailAddress.tsx +1 -1
  128. package/apps/shared/components/mail/MailListToolbar.tsx +2 -2
  129. package/apps/shared/components/mail/MailSelectionBar.tsx +240 -240
  130. package/apps/shared/components/mail/MenuButton.tsx +404 -404
  131. package/apps/shared/components/mail/MessageDetailPane.tsx +1759 -1757
  132. package/apps/shared/components/mail/MoveToFolderDialog.tsx +8 -6
  133. package/apps/shared/components/mail/OutboxRowStatus.tsx +1 -1
  134. package/apps/shared/components/mail/PermanentDeleteDialog.tsx +2 -2
  135. package/apps/shared/components/mail/compose/ComposeContext.tsx +323 -312
  136. package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -422
  137. package/apps/shared/components/mail/compose/ComposeWindow.tsx +1709 -1681
  138. package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +4 -4
  139. package/apps/shared/components/mail/compose/EmojiPicker.tsx +2 -2
  140. package/apps/shared/components/mail/compose/GifPicker.tsx +3 -3
  141. package/apps/shared/components/mail/compose/RecipientInput.tsx +1 -1
  142. package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -147
  143. package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +1 -1
  144. package/apps/shared/components/mail/compose/composeFlushRegistry.ts +60 -60
  145. package/apps/shared/components/mail/compose/encryptionRequirement.ts +2 -2
  146. package/apps/shared/components/mail/compose/quotedBody.ts +161 -161
  147. package/apps/shared/components/mail/compose/recipients.ts +1 -1
  148. package/apps/shared/components/mail/invite/InviteRowChip.tsx +2 -2
  149. package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +9 -7
  150. package/apps/shared/components/mail/invite/InviteTimeline.tsx +1 -1
  151. package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +2 -2
  152. package/apps/shared/components/mail/invite/inviteFormat.ts +2 -2
  153. package/apps/shared/components/mail/invite/inviteStore.ts +14 -6
  154. package/apps/shared/components/mail/labelMenu.tsx +9 -7
  155. package/apps/shared/components/mail/layout/MailShell.tsx +4 -4
  156. package/apps/shared/components/mail/listPreferences.ts +2 -2
  157. package/apps/shared/components/mail/pinnedSigners.ts +45 -19
  158. package/apps/shared/components/mail/reading/EncryptedBody.tsx +2 -2
  159. package/apps/shared/components/mail/reading/MessageBody.tsx +6 -6
  160. package/apps/shared/components/mail/reading/MessageCard.tsx +3 -3
  161. package/apps/shared/components/mail/reading/MessageMoreMenu.tsx +258 -258
  162. package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +79 -79
  163. package/apps/shared/components/mail/reading/PendingMessageCard.tsx +1 -1
  164. package/apps/shared/components/mail/reading/bodyContent.ts +3 -3
  165. package/apps/shared/components/mail/reading/bodyHtml.ts +1 -1
  166. package/apps/shared/components/mail/reading/messageExport.ts +59 -59
  167. package/apps/shared/components/mail/reading/printMessage.ts +99 -99
  168. package/apps/shared/components/mail/reading/useMessageActions.ts +450 -443
  169. package/apps/shared/components/mail/unreadStyle.tsx +1 -1
  170. package/apps/shared/components/mail/verificationSeals.ts +129 -125
  171. package/apps/shared/components/mail/writableMailboxes.ts +14 -7
  172. package/apps/shared/components/rules/RuleBuilder.tsx +311 -311
  173. package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -344
  174. package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
  175. package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
  176. package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
  177. package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
  178. package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
  179. package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
  180. package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -51
  181. package/apps/shared/keyboard/ShortcutProvider.tsx +62 -62
  182. package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -84
  183. package/apps/shared/keyboard/dispatch.ts +124 -124
  184. package/apps/shared/keyboard/format.ts +89 -89
  185. package/apps/shared/keyboard/keymap.ts +114 -114
  186. package/apps/shared/keyboard/registry.ts +65 -65
  187. package/apps/shared/keyboard/targets.ts +79 -79
  188. package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
  189. package/apps/shared/mail/folderCounts.ts +8 -4
  190. package/apps/shared/mail/folderOfType.ts +51 -49
  191. package/apps/shared/mail/folderTree.ts +143 -143
  192. package/apps/shared/mail/listAllPages.ts +39 -39
  193. package/apps/shared/mail/listSnapshots.ts +2 -2
  194. package/apps/shared/mail/messageReadState.ts +6 -5
  195. package/apps/shared/mail/newMailNotifications.ts +171 -171
  196. package/apps/shared/mail/outbox/composeBridge.ts +2 -2
  197. package/apps/shared/mail/outbox/outboxState.ts +1 -1
  198. package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
  199. package/apps/shared/mail/outbox/sendDecision.ts +2 -2
  200. package/apps/shared/mail/outbox/sendJob.ts +450 -445
  201. package/apps/shared/mail/outbox/sendOutcomes.ts +160 -155
  202. package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
  203. package/apps/shared/mail/permanentDelete.ts +8 -7
  204. package/apps/shared/mail/primaryMailbox.ts +1 -1
  205. package/apps/shared/mail/reportNotices.ts +66 -66
  206. package/apps/shared/mail/senderBlocking.ts +157 -141
  207. package/apps/shared/mail/useMailConnection.ts +207 -205
  208. package/apps/shared/mail/useMailLiveUpdates.ts +277 -277
  209. package/apps/shared/mail/useMailboxUpdateAccess.ts +84 -60
  210. package/apps/shared/mail/useMarkMessageRead.ts +54 -47
  211. package/apps/shared/mail/useNewMailNotifications.ts +178 -178
  212. package/apps/shared/mail/usePermanentDelete.tsx +5 -3
  213. package/apps/shared/navigation/pageTitle.ts +1 -1
  214. package/apps/shared/notifications/NotificationCenter.tsx +1 -1
  215. package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
  216. package/apps/shared/notifications/apiErrors.ts +2 -2
  217. package/apps/shared/notifications/pushStatus.ts +1 -1
  218. package/apps/shared/notifications/store.ts +560 -560
  219. package/apps/shared/notifications/systemErrors.ts +1 -1
  220. package/apps/shared/search/LocalIndexLifecycle.tsx +120 -114
  221. package/apps/shared/search/crossMailboxSearch.ts +3 -3
  222. package/apps/shared/search/localIndexBuilder.ts +493 -481
  223. package/apps/shared/search/localIndexKey.ts +1 -1
  224. package/apps/shared/search/localIndexRpcClient.ts +150 -30
  225. package/apps/shared/search/localIndexTransport.ts +84 -0
  226. package/apps/shared/search/localIndexWorker.ts +1 -1
  227. package/apps/shared/search/searchTier2.ts +3 -3
  228. package/apps/shared/signing/enrollmentStorage.ts +33 -33
  229. package/apps/shared/signing/enrollmentTracker.ts +385 -385
  230. package/apps/shared/signing/enrollmentView.ts +251 -251
  231. package/apps/shared/signing/signingInfo.ts +1 -1
  232. package/apps/shared/signing/useNow.ts +19 -19
  233. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +92 -90
  234. package/apps/shared/styles/app.css +387 -396
  235. package/apps/www/_layout.tsx +2 -2
  236. package/apps/www/calendar/index.tsx +583 -581
  237. package/apps/www/contacts/[uid].tsx +114 -112
  238. package/apps/www/contacts/index.tsx +19 -17
  239. package/apps/www/index.tsx +3021 -3012
  240. package/apps/www/messages/[uid].tsx +141 -139
  241. package/apps/www/settings/auto-reply/index.tsx +138 -136
  242. package/apps/www/settings/blocked-senders/index.tsx +305 -303
  243. package/apps/www/settings/encryption/index.tsx +1296 -1290
  244. package/apps/www/settings/filters/[uid].tsx +184 -179
  245. package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
  246. package/apps/www/settings/filters/index.tsx +105 -105
  247. package/apps/www/settings/filters/new/index.tsx +167 -165
  248. package/apps/www/settings/labels/index.tsx +209 -207
  249. package/apps/www/settings/privacy/index.tsx +499 -495
  250. package/apps/www/settings/profile/index.tsx +256 -251
  251. package/apps/www/settings/read-receipts/index.tsx +155 -150
  252. package/apps/www/settings/sharing/index.tsx +259 -259
  253. package/apps/www/settings/signatures/[uid].tsx +175 -175
  254. package/apps/www/settings/signatures/index.tsx +91 -91
  255. package/apps/www/settings/signatures/new/index.tsx +138 -138
  256. package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
  257. package/apps/www/tasks/index.tsx +659 -654
  258. package/dist/apps/admin/_layout.d.ts +1 -1
  259. package/dist/apps/admin/_layout.js +1 -1
  260. package/dist/apps/admin/audit-log/index.d.ts +1 -1
  261. package/dist/apps/admin/audit-log/index.js +4 -4
  262. package/dist/apps/admin/branding/index.js +3 -3
  263. package/dist/apps/admin/data-requests/index.js +8 -8
  264. package/dist/apps/admin/distribution-lists/[uid].js +3 -3
  265. package/dist/apps/admin/distribution-lists/index.js +4 -4
  266. package/dist/apps/admin/distribution-lists/new/index.js +5 -5
  267. package/dist/apps/admin/domains/[uid].js +6 -6
  268. package/dist/apps/admin/domains/index.js +4 -4
  269. package/dist/apps/admin/domains/new/index.js +5 -5
  270. package/dist/apps/admin/encryption-policy/index.js +1 -1
  271. package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
  272. package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
  273. package/dist/apps/admin/escrow-scopes/index.js +4 -4
  274. package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
  275. package/dist/apps/admin/index.js +6 -6
  276. package/dist/apps/admin/ingest-queue/index.js +4 -4
  277. package/dist/apps/admin/mailbox-policy/index.js +1 -1
  278. package/dist/apps/admin/mailboxes/[uid].js +5 -5
  279. package/dist/apps/admin/quarantine/index.js +5 -5
  280. package/dist/apps/admin/retention-policy/index.js +3 -3
  281. package/dist/apps/admin/signing-certificates/index.js +5 -5
  282. package/dist/apps/admin/transport-rules/[uid].js +5 -5
  283. package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
  284. package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
  285. package/dist/apps/admin/transport-rules/index.js +4 -4
  286. package/dist/apps/admin/transport-rules/new/index.js +5 -5
  287. package/dist/apps/escrow/_layout.d.ts +1 -1
  288. package/dist/apps/escrow/audit-log/index.d.ts +1 -1
  289. package/dist/apps/escrow/audit-log/index.js +4 -4
  290. package/dist/apps/escrow/index.js +4 -4
  291. package/dist/apps/escrow/matters/[uid].js +10 -10
  292. package/dist/apps/escrow/matters/new/index.js +6 -6
  293. package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
  294. package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
  295. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
  296. package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
  297. package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
  298. package/dist/apps/shared/appearance/appearanceCache.js +1 -1
  299. package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
  300. package/dist/apps/shared/appearance/theme.d.ts +1 -1
  301. package/dist/apps/shared/auth/adminAccess.js +1 -1
  302. package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
  303. package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
  304. package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
  305. package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
  306. package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
  307. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
  308. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
  309. package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
  310. package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
  311. package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
  312. package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
  313. package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
  314. package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
  315. package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
  316. package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
  317. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
  318. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
  319. package/dist/apps/shared/components/admin/elevation.js +1 -1
  320. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
  321. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  322. package/dist/apps/shared/components/admin/layout/AdminShell.js +8 -8
  323. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
  324. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
  325. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
  326. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
  327. package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
  328. package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
  329. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
  330. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
  331. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
  332. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
  333. package/dist/apps/shared/components/admin/settings/BrandingForm.js +8 -8
  334. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
  335. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
  336. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
  337. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
  338. package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
  339. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
  340. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
  341. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
  342. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
  343. package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
  344. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
  345. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
  346. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
  347. package/dist/apps/shared/components/admin/setup/SetupWizard.js +26 -22
  348. package/dist/apps/shared/components/admin/signOut.js +1 -1
  349. package/dist/apps/shared/components/admin/usePagedList.js +2 -2
  350. package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
  351. package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
  352. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
  353. package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
  354. package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
  355. package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
  356. package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
  357. package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
  358. package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
  359. package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
  360. package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
  361. package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
  362. package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
  363. package/dist/apps/shared/components/calendar/EventModal.js +3 -3
  364. package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
  365. package/dist/apps/shared/components/calendar/EventShell.js +1 -1
  366. package/dist/apps/shared/components/calendar/FindATime.js +6 -4
  367. package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
  368. package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
  369. package/dist/apps/shared/components/calendar/MonthView.js +1 -1
  370. package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
  371. package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
  372. package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
  373. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
  374. package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
  375. package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
  376. package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
  377. package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
  378. package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
  379. package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
  380. package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
  381. package/dist/apps/shared/components/calendar/TimeGridView.js +1 -1
  382. package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
  383. package/dist/apps/shared/components/calendar/allDay.js +1 -1
  384. package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
  385. package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
  386. package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
  387. package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
  388. package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
  389. package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
  390. package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
  391. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
  392. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
  393. package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
  394. package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
  395. package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
  396. package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
  397. package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
  398. package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
  399. package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
  400. package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
  401. package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
  402. package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
  403. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
  404. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
  405. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -7
  406. package/dist/apps/shared/components/forms/StringListField.js +1 -1
  407. package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
  408. package/dist/apps/shared/components/layout/AppShell.js +14 -10
  409. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
  410. package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
  411. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
  412. package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
  413. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
  414. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
  415. package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
  416. package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
  417. package/dist/apps/shared/components/layout/UserMenu.js +6 -4
  418. package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
  419. package/dist/apps/shared/components/mail/ConversationList.js +6 -4
  420. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
  421. package/dist/apps/shared/components/mail/ConversationThreadPane.js +13 -11
  422. package/dist/apps/shared/components/mail/InviteCard.js +10 -8
  423. package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
  424. package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
  425. package/dist/apps/shared/components/mail/MailAddress.js +1 -1
  426. package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
  427. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
  428. package/dist/apps/shared/components/mail/MenuButton.js +1 -1
  429. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
  430. package/dist/apps/shared/components/mail/MessageDetailPane.js +30 -28
  431. package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
  432. package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
  433. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
  434. package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
  435. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
  436. package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
  437. package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +7 -1
  438. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +54 -31
  439. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
  440. package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
  441. package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
  442. package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
  443. package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
  444. package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
  445. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
  446. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
  447. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
  448. package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
  449. package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
  450. package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +1 -1
  451. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +1 -1
  452. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +9 -7
  453. package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
  454. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
  455. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
  456. package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
  457. package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
  458. package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
  459. package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
  460. package/dist/apps/shared/components/mail/labelMenu.js +9 -7
  461. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
  462. package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
  463. package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
  464. package/dist/apps/shared/components/mail/listPreferences.js +1 -1
  465. package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
  466. package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
  467. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
  468. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
  469. package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
  470. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
  471. package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
  472. package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
  473. package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
  474. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
  475. package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
  476. package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
  477. package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
  478. package/dist/apps/shared/components/mail/reading/printMessage.d.ts +2 -2
  479. package/dist/apps/shared/components/mail/reading/printMessage.js +11 -11
  480. package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
  481. package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
  482. package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
  483. package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
  484. package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
  485. package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
  486. package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
  487. package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
  488. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
  489. package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
  490. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
  491. package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
  492. package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
  493. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
  494. package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
  495. package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
  496. package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
  497. package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
  498. package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
  499. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
  500. package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
  501. package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
  502. package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
  503. package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
  504. package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
  505. package/dist/apps/shared/mail/folderCounts.js +6 -4
  506. package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
  507. package/dist/apps/shared/mail/folderOfType.js +4 -4
  508. package/dist/apps/shared/mail/folderTree.d.ts +1 -1
  509. package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
  510. package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
  511. package/dist/apps/shared/mail/messageReadState.js +5 -5
  512. package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
  513. package/dist/apps/shared/mail/newMailNotifications.js +1 -1
  514. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
  515. package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
  516. package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
  517. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
  518. package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
  519. package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
  520. package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
  521. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
  522. package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
  523. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
  524. package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
  525. package/dist/apps/shared/mail/permanentDelete.js +8 -8
  526. package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
  527. package/dist/apps/shared/mail/primaryMailbox.js +1 -1
  528. package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
  529. package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
  530. package/dist/apps/shared/mail/senderBlocking.js +19 -16
  531. package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
  532. package/dist/apps/shared/mail/useMailConnection.js +12 -10
  533. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
  534. package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
  535. package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
  536. package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
  537. package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
  538. package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
  539. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
  540. package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
  541. package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
  542. package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
  543. package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
  544. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
  545. package/dist/apps/shared/notifications/apiErrors.js +2 -2
  546. package/dist/apps/shared/notifications/pushStatus.js +1 -1
  547. package/dist/apps/shared/notifications/systemErrors.js +1 -1
  548. package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
  549. package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
  550. package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
  551. package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
  552. package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
  553. package/dist/apps/shared/search/localIndexBuilder.js +15 -12
  554. package/dist/apps/shared/search/localIndexKey.js +1 -1
  555. package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
  556. package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
  557. package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
  558. package/dist/apps/shared/search/localIndexTransport.js +1 -0
  559. package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
  560. package/dist/apps/shared/search/searchTier2.d.ts +3 -3
  561. package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
  562. package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
  563. package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
  564. package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
  565. package/dist/apps/shared/signing/signingInfo.js +1 -1
  566. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
  567. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
  568. package/dist/apps/shared/styles/app.css +387 -396
  569. package/dist/apps/www/_layout.d.ts +1 -1
  570. package/dist/apps/www/_layout.js +1 -1
  571. package/dist/apps/www/calendar/index.d.ts +1 -1
  572. package/dist/apps/www/calendar/index.js +20 -18
  573. package/dist/apps/www/contacts/[uid].d.ts +1 -1
  574. package/dist/apps/www/contacts/[uid].js +8 -6
  575. package/dist/apps/www/contacts/index.d.ts +1 -1
  576. package/dist/apps/www/contacts/index.js +19 -17
  577. package/dist/apps/www/index.d.ts +1 -1
  578. package/dist/apps/www/index.js +54 -50
  579. package/dist/apps/www/messages/[uid].d.ts +1 -1
  580. package/dist/apps/www/messages/[uid].js +11 -9
  581. package/dist/apps/www/settings/appearance/index.d.ts +1 -1
  582. package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
  583. package/dist/apps/www/settings/auto-reply/index.js +6 -4
  584. package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
  585. package/dist/apps/www/settings/blocked-senders/index.js +6 -4
  586. package/dist/apps/www/settings/encryption/index.d.ts +1 -1
  587. package/dist/apps/www/settings/encryption/index.js +31 -29
  588. package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
  589. package/dist/apps/www/settings/filters/[uid].js +11 -9
  590. package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
  591. package/dist/apps/www/settings/filters/index.d.ts +1 -1
  592. package/dist/apps/www/settings/filters/index.js +4 -4
  593. package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
  594. package/dist/apps/www/settings/filters/new/index.js +10 -8
  595. package/dist/apps/www/settings/labels/index.d.ts +1 -1
  596. package/dist/apps/www/settings/labels/index.js +12 -10
  597. package/dist/apps/www/settings/privacy/index.d.ts +1 -1
  598. package/dist/apps/www/settings/privacy/index.js +18 -14
  599. package/dist/apps/www/settings/profile/index.d.ts +1 -1
  600. package/dist/apps/www/settings/profile/index.js +7 -5
  601. package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
  602. package/dist/apps/www/settings/read-receipts/index.js +5 -3
  603. package/dist/apps/www/settings/sharing/index.d.ts +1 -1
  604. package/dist/apps/www/settings/sharing/index.js +6 -6
  605. package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
  606. package/dist/apps/www/settings/signatures/[uid].js +5 -5
  607. package/dist/apps/www/settings/signatures/index.d.ts +1 -1
  608. package/dist/apps/www/settings/signatures/index.js +4 -4
  609. package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
  610. package/dist/apps/www/settings/signatures/new/index.js +4 -4
  611. package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
  612. package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
  613. package/dist/apps/www/tasks/index.d.ts +1 -1
  614. package/dist/apps/www/tasks/index.js +17 -15
  615. package/dist/lib/admin/auditLogApi.d.ts +32 -0
  616. package/dist/lib/admin/auditLogApi.js +27 -0
  617. package/dist/lib/admin/distributionListsApi.d.ts +47 -0
  618. package/dist/lib/admin/distributionListsApi.js +34 -0
  619. package/dist/lib/admin/domainsApi.d.ts +75 -0
  620. package/dist/lib/admin/domainsApi.js +44 -0
  621. package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
  622. package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
  623. package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
  624. package/dist/lib/admin/escrowAuditLogApi.js +31 -0
  625. package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
  626. package/dist/lib/admin/escrowScopesApi.js +50 -0
  627. package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
  628. package/dist/lib/admin/leftoverMailboxApi.js +67 -0
  629. package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
  630. package/dist/lib/admin/mailboxPolicyApi.js +21 -0
  631. package/dist/lib/admin/matterExportApi.d.ts +40 -0
  632. package/dist/lib/admin/matterExportApi.js +39 -0
  633. package/dist/lib/admin/matterSearchApi.d.ts +20 -0
  634. package/dist/lib/admin/matterSearchApi.js +27 -0
  635. package/dist/lib/admin/mattersApi.d.ts +56 -0
  636. package/dist/lib/admin/mattersApi.js +40 -0
  637. package/dist/lib/admin/pluginsApi.d.ts +265 -0
  638. package/dist/lib/admin/pluginsApi.js +102 -0
  639. package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
  640. package/dist/lib/admin/retentionPolicyApi.js +29 -0
  641. package/dist/lib/admin/setupApi.d.ts +23 -0
  642. package/dist/lib/admin/setupApi.js +26 -0
  643. package/dist/lib/admin/transportRulesApi.d.ts +66 -0
  644. package/dist/lib/admin/transportRulesApi.js +43 -0
  645. package/dist/lib/appearance/preferencesApi.d.ts +128 -0
  646. package/dist/lib/appearance/preferencesApi.js +257 -0
  647. package/dist/lib/auth/profileApi.d.ts +33 -0
  648. package/dist/lib/auth/profileApi.js +52 -0
  649. package/dist/lib/auth/session.d.ts +70 -0
  650. package/dist/lib/auth/session.js +210 -0
  651. package/dist/lib/branding/brandingApi.d.ts +47 -0
  652. package/dist/lib/branding/brandingApi.js +61 -0
  653. package/dist/lib/branding/useBranding.d.ts +23 -0
  654. package/dist/lib/branding/useBranding.js +65 -0
  655. package/dist/lib/calendar/calendarApi.d.ts +230 -0
  656. package/dist/lib/calendar/calendarApi.js +106 -0
  657. package/dist/lib/calendar/calendarColors.d.ts +18 -0
  658. package/dist/lib/calendar/calendarColors.js +39 -0
  659. package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
  660. package/dist/lib/calendar/calendarDragIds.js +92 -0
  661. package/dist/lib/calendar/calendarMutations.d.ts +68 -0
  662. package/dist/lib/calendar/calendarMutations.js +190 -0
  663. package/dist/lib/calendar/eventDescription.d.ts +43 -0
  664. package/dist/lib/calendar/eventDescription.js +195 -0
  665. package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
  666. package/dist/lib/calendar/freeBusyApi.js +119 -0
  667. package/dist/lib/calendar/inviteApi.d.ts +121 -0
  668. package/dist/lib/calendar/inviteApi.js +54 -0
  669. package/dist/lib/calendar/recurrence.d.ts +60 -0
  670. package/dist/lib/calendar/recurrence.js +225 -0
  671. package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
  672. package/dist/lib/components/avatar/ContactAvatar.js +26 -0
  673. package/dist/lib/components/buttons/Button.d.ts +9 -0
  674. package/dist/lib/components/buttons/Button.js +14 -0
  675. package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
  676. package/dist/lib/components/buttons/CopyButton.js +14 -0
  677. package/dist/lib/components/feedback/Alert.d.ts +4 -0
  678. package/dist/lib/components/feedback/Alert.js +4 -0
  679. package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
  680. package/dist/lib/components/feedback/Skeleton.js +14 -0
  681. package/dist/lib/components/forms/FormField.d.ts +8 -0
  682. package/dist/lib/components/forms/FormField.js +5 -0
  683. package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
  684. package/dist/lib/components/navigation/BottomTabBar.js +32 -0
  685. package/dist/lib/components/overlays/Drawer.d.ts +20 -0
  686. package/dist/lib/components/overlays/Drawer.js +27 -0
  687. package/dist/lib/components/overlays/Modal.d.ts +17 -0
  688. package/dist/lib/components/overlays/Modal.js +25 -0
  689. package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
  690. package/dist/lib/components/overlays/PopoverPortal.js +53 -0
  691. package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
  692. package/dist/lib/components/overlays/overlayStack.js +143 -0
  693. package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
  694. package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
  695. package/dist/lib/contacts/contactsApi.d.ts +174 -0
  696. package/dist/lib/contacts/contactsApi.js +139 -0
  697. package/dist/lib/contacts/vcard.d.ts +23 -0
  698. package/dist/lib/contacts/vcard.js +179 -0
  699. package/dist/lib/crypto/composeSecurity.d.ts +67 -0
  700. package/dist/lib/crypto/composeSecurity.js +93 -0
  701. package/dist/lib/crypto/encoding.d.ts +9 -0
  702. package/dist/lib/crypto/encoding.js +32 -0
  703. package/dist/lib/crypto/escrowKeys.d.ts +25 -0
  704. package/dist/lib/crypto/escrowKeys.js +59 -0
  705. package/dist/lib/crypto/idleTimeout.d.ts +16 -0
  706. package/dist/lib/crypto/idleTimeout.js +47 -0
  707. package/dist/lib/crypto/keySession.d.ts +159 -0
  708. package/dist/lib/crypto/keySession.js +367 -0
  709. package/dist/lib/crypto/keys.d.ts +50 -0
  710. package/dist/lib/crypto/keys.js +89 -0
  711. package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
  712. package/dist/lib/crypto/keyvaultApi.js +350 -0
  713. package/dist/lib/crypto/masterKey.d.ts +59 -0
  714. package/dist/lib/crypto/masterKey.js +104 -0
  715. package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
  716. package/dist/lib/crypto/masterKeyWraps.js +228 -0
  717. package/dist/lib/crypto/messageSecurity.d.ts +248 -0
  718. package/dist/lib/crypto/messageSecurity.js +367 -0
  719. package/dist/lib/crypto/mime.d.ts +143 -0
  720. package/dist/lib/crypto/mime.js +504 -0
  721. package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
  722. package/dist/lib/crypto/passkeyUnlock.js +79 -0
  723. package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
  724. package/dist/lib/crypto/passwordUnlock.js +73 -0
  725. package/dist/lib/crypto/recoveryCode.d.ts +28 -0
  726. package/dist/lib/crypto/recoveryCode.js +86 -0
  727. package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
  728. package/dist/lib/crypto/signingProviderApi.js +60 -0
  729. package/dist/lib/crypto/smime.d.ts +128 -0
  730. package/dist/lib/crypto/smime.js +402 -0
  731. package/dist/lib/crypto/smimeMessage.d.ts +132 -0
  732. package/dist/lib/crypto/smimeMessage.js +341 -0
  733. package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
  734. package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
  735. package/dist/lib/crypto/verificationSeal.d.ts +56 -0
  736. package/dist/lib/crypto/verificationSeal.js +220 -0
  737. package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
  738. package/dist/lib/mail/compose/composeQuoting.js +204 -0
  739. package/dist/lib/mail/compose/emojiData.d.ts +9 -0
  740. package/dist/lib/mail/compose/emojiData.js +18 -0
  741. package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
  742. package/dist/lib/mail/compose/giphyApi.js +20 -0
  743. package/dist/lib/mail/conversationsApi.d.ts +80 -0
  744. package/dist/lib/mail/conversationsApi.js +44 -0
  745. package/dist/lib/mail/dataExportApi.d.ts +50 -0
  746. package/dist/lib/mail/dataExportApi.js +40 -0
  747. package/dist/lib/mail/directoryApi.d.ts +49 -0
  748. package/dist/lib/mail/directoryApi.js +101 -0
  749. package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
  750. package/dist/lib/mail/erasureRequestApi.js +43 -0
  751. package/dist/lib/mail/flaggedMessages.d.ts +22 -0
  752. package/dist/lib/mail/flaggedMessages.js +73 -0
  753. package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
  754. package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
  755. package/dist/lib/mail/labelsApi.d.ts +47 -0
  756. package/dist/lib/mail/labelsApi.js +38 -0
  757. package/dist/lib/mail/mailAddress.d.ts +27 -0
  758. package/dist/lib/mail/mailAddress.js +54 -0
  759. package/dist/lib/mail/mailApi.d.ts +850 -0
  760. package/dist/lib/mail/mailApi.js +602 -0
  761. package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
  762. package/dist/lib/mail/mailDetailHooks.js +53 -0
  763. package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
  764. package/dist/lib/mail/mailFilterRulesApi.js +45 -0
  765. package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
  766. package/dist/lib/mail/mailSignaturesApi.js +42 -0
  767. package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
  768. package/dist/lib/mail/mailboxAccessApi.js +56 -0
  769. package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
  770. package/dist/lib/mail/mailboxImportApi.js +51 -0
  771. package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
  772. package/dist/lib/mail/messageBodySanitizer.js +118 -0
  773. package/dist/lib/mail/pushClient.d.ts +106 -0
  774. package/dist/lib/mail/pushClient.js +317 -0
  775. package/dist/lib/mail/sendEvents.d.ts +33 -0
  776. package/dist/lib/mail/sendEvents.js +44 -0
  777. package/dist/lib/mail/sendFailure.d.ts +16 -0
  778. package/dist/lib/mail/sendFailure.js +86 -0
  779. package/dist/lib/mail/senderListsApi.d.ts +69 -0
  780. package/dist/lib/mail/senderListsApi.js +126 -0
  781. package/dist/lib/search/queryGrammar.d.ts +51 -0
  782. package/dist/lib/search/queryGrammar.js +111 -0
  783. package/dist/lib/search/searchApi.d.ts +100 -0
  784. package/dist/lib/search/searchApi.js +107 -0
  785. package/dist/lib/search/searchScoring.d.ts +34 -0
  786. package/dist/lib/search/searchScoring.js +44 -0
  787. package/dist/lib/search/searchTier3.d.ts +64 -0
  788. package/dist/lib/search/searchTier3.js +335 -0
  789. package/dist/lib/tasks/tasksApi.d.ts +85 -0
  790. package/dist/lib/tasks/tasksApi.js +55 -0
  791. package/dist/lib/util/api.d.ts +160 -0
  792. package/dist/lib/util/api.js +251 -0
  793. package/dist/lib/util/apiClientContext.d.ts +7 -0
  794. package/dist/lib/util/apiClientContext.js +32 -0
  795. package/dist/lib/util/apiQuery.d.ts +32 -0
  796. package/dist/lib/util/apiQuery.js +42 -0
  797. package/dist/lib/util/clipboard.d.ts +7 -0
  798. package/dist/lib/util/clipboard.js +56 -0
  799. package/dist/lib/util/dateInput.d.ts +6 -0
  800. package/dist/lib/util/dateInput.js +14 -0
  801. package/dist/lib/util/timeZone.d.ts +12 -0
  802. package/dist/lib/util/timeZone.js +32 -0
  803. package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
  804. package/dist/lib/util/useCopyToClipboard.js +34 -0
  805. package/dist/lib/util/useIsMobile.d.ts +10 -0
  806. package/dist/lib/util/useIsMobile.js +29 -0
  807. package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
  808. package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
  809. package/lib/admin/auditLogApi.ts +48 -0
  810. package/lib/admin/distributionListsApi.ts +77 -0
  811. package/lib/admin/domainsApi.ts +113 -0
  812. package/lib/admin/escrowAccessRequestsApi.ts +110 -0
  813. package/lib/admin/escrowAuditLogApi.ts +101 -0
  814. package/lib/admin/escrowScopesApi.ts +112 -0
  815. package/lib/admin/leftoverMailboxApi.ts +120 -0
  816. package/lib/admin/mailboxPolicyApi.ts +38 -0
  817. package/lib/admin/matterExportApi.ts +62 -0
  818. package/lib/admin/matterSearchApi.ts +35 -0
  819. package/lib/admin/mattersApi.ts +86 -0
  820. package/lib/admin/pluginsApi.ts +339 -0
  821. package/lib/admin/retentionPolicyApi.ts +46 -0
  822. package/lib/admin/setupApi.ts +40 -0
  823. package/lib/admin/transportRulesApi.ts +106 -0
  824. package/lib/appearance/preferencesApi.ts +335 -0
  825. package/lib/auth/profileApi.ts +77 -0
  826. package/lib/auth/session.ts +238 -0
  827. package/lib/branding/brandingApi.ts +97 -0
  828. package/lib/branding/useBranding.ts +81 -0
  829. package/lib/calendar/calendarApi.ts +309 -0
  830. package/lib/calendar/calendarColors.ts +45 -0
  831. package/lib/calendar/calendarDragIds.ts +112 -0
  832. package/lib/calendar/calendarMutations.ts +236 -0
  833. package/lib/calendar/eventDescription.ts +211 -0
  834. package/lib/calendar/freeBusyApi.ts +197 -0
  835. package/lib/calendar/inviteApi.ts +154 -0
  836. package/lib/calendar/recurrence.ts +285 -0
  837. package/lib/components/avatar/ContactAvatar.tsx +47 -0
  838. package/lib/components/buttons/Button.tsx +40 -0
  839. package/lib/components/buttons/CopyButton.tsx +45 -0
  840. package/lib/components/feedback/Alert.tsx +13 -0
  841. package/lib/components/feedback/Skeleton.tsx +38 -0
  842. package/lib/components/forms/FormField.tsx +23 -0
  843. package/lib/components/navigation/BottomTabBar.tsx +66 -0
  844. package/lib/components/overlays/Drawer.tsx +72 -0
  845. package/lib/components/overlays/Modal.tsx +64 -0
  846. package/lib/components/overlays/PopoverPortal.tsx +74 -0
  847. package/lib/components/overlays/overlayStack.ts +165 -0
  848. package/lib/components/pickers/MiniDatePicker.tsx +90 -0
  849. package/lib/contacts/contactsApi.ts +289 -0
  850. package/lib/contacts/vcard.ts +197 -0
  851. package/lib/crypto/composeSecurity.ts +140 -0
  852. package/lib/crypto/encoding.ts +36 -0
  853. package/lib/crypto/escrowKeys.ts +74 -0
  854. package/lib/crypto/idleTimeout.ts +49 -0
  855. package/lib/crypto/keySession.ts +483 -0
  856. package/lib/crypto/keys.ts +105 -0
  857. package/lib/crypto/keyvaultApi.ts +674 -0
  858. package/lib/crypto/masterKey.ts +134 -0
  859. package/lib/crypto/masterKeyWraps.ts +269 -0
  860. package/lib/crypto/messageSecurity.ts +600 -0
  861. package/lib/crypto/mime.ts +568 -0
  862. package/lib/crypto/passkeyUnlock.ts +103 -0
  863. package/lib/crypto/passwordUnlock.ts +98 -0
  864. package/lib/crypto/recoveryCode.ts +93 -0
  865. package/lib/crypto/signingProviderApi.ts +135 -0
  866. package/lib/crypto/smime.ts +459 -0
  867. package/lib/crypto/smimeMessage.ts +491 -0
  868. package/lib/crypto/useIdleKeyTimeout.ts +176 -0
  869. package/lib/crypto/verificationSeal.ts +288 -0
  870. package/lib/mail/compose/composeQuoting.ts +243 -0
  871. package/lib/mail/compose/emojiData.ts +34 -0
  872. package/lib/mail/compose/giphyApi.ts +31 -0
  873. package/lib/mail/conversationsApi.ts +123 -0
  874. package/lib/mail/dataExportApi.ts +73 -0
  875. package/lib/mail/directoryApi.ts +161 -0
  876. package/lib/mail/erasureRequestApi.ts +71 -0
  877. package/lib/mail/flaggedMessages.ts +80 -0
  878. package/lib/mail/focusedInboxOverridesApi.ts +64 -0
  879. package/lib/mail/labelsApi.ts +71 -0
  880. package/lib/mail/mailAddress.ts +76 -0
  881. package/lib/mail/mailApi.ts +1286 -0
  882. package/lib/mail/mailDetailHooks.ts +57 -0
  883. package/lib/mail/mailFilterRulesApi.ts +116 -0
  884. package/lib/mail/mailSignaturesApi.ts +84 -0
  885. package/lib/mail/mailboxAccessApi.ts +106 -0
  886. package/lib/mail/mailboxImportApi.ts +87 -0
  887. package/lib/mail/messageBodySanitizer.ts +129 -0
  888. package/lib/mail/pushClient.ts +375 -0
  889. package/lib/mail/sendEvents.ts +72 -0
  890. package/lib/mail/sendFailure.ts +98 -0
  891. package/lib/mail/senderListsApi.ts +161 -0
  892. package/lib/search/queryGrammar.ts +179 -0
  893. package/lib/search/searchApi.ts +199 -0
  894. package/lib/search/searchScoring.ts +46 -0
  895. package/lib/search/searchTier3.ts +406 -0
  896. package/lib/tasks/tasksApi.ts +128 -0
  897. package/lib/util/api.ts +318 -0
  898. package/lib/util/apiClientContext.ts +35 -0
  899. package/lib/util/apiQuery.ts +61 -0
  900. package/lib/util/clipboard.ts +55 -0
  901. package/lib/util/dateInput.ts +15 -0
  902. package/lib/util/timeZone.ts +33 -0
  903. package/lib/util/useCopyToClipboard.ts +48 -0
  904. package/lib/util/useIsMobile.ts +34 -0
  905. package/lib/videoconf/videoMeetingsApi.ts +144 -0
  906. package/package.json +10 -4
@@ -1,581 +1,583 @@
1
- ///////////////////////////////////////////////////////////////////////////////
2
- // Copyright (C) 2026 Jean-Philippe Steinmetz
3
- // SPDX-License-Identifier: MPL-2.0
4
- ///////////////////////////////////////////////////////////////////////////////
5
- import { pageTitle } from "../../shared/navigation/pageTitle.js";
6
- import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
7
- import { DndContext, DragEndEvent, MouseSensor, TouchSensor, useSensor, useSensors } from "@dnd-kit/core";
8
- import { HiOutlineBars3, HiOutlinePlus } from "react-icons/hi2";
9
- import {
10
- addDays,
11
- addMonths,
12
- addWeeks,
13
- eachDayOfInterval,
14
- endOfDay,
15
- endOfMonth,
16
- endOfWeek,
17
- format,
18
- parseISO,
19
- startOfDay,
20
- startOfMonth,
21
- startOfWeek,
22
- } from "date-fns";
23
- import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
24
- import useIsMobile from "@rapidmx/react-shared/util/useIsMobile.js";
25
- import { CalendarEvent, getCalendarEvent, listCalendarEvents } from "@rapidmx/react-shared/calendar/calendarApi.js";
26
- import { getPushClient } from "@rapidmx/react-shared/mail/pushClient.js";
27
- import { moveOccurrence, resizeOccurrenceEnd } from "@rapidmx/react-shared/calendar/calendarMutations.js";
28
- import { resolveDragAction } from "@rapidmx/react-shared/calendar/calendarDragIds.js";
29
- import { createFolder } from "@rapidmx/react-shared/mail/mailApi.js";
30
- import { CalendarOccurrence, expandAllOccurrences } from "@rapidmx/react-shared/calendar/recurrence.js";
31
- import Drawer from "@rapidmx/react-shared/components/overlays/Drawer.js";
32
- import CalendarShell, { CalendarShellProps, useCalendarShell } from "../../shared/components/calendar/layout/CalendarShell.js";
33
- import CalendarListSidebar from "../../shared/components/calendar/CalendarListSidebar.js";
34
- import { useWritableMailboxes } from "../../shared/components/mail/writableMailboxes.js";
35
- import EventModal from "../../shared/components/calendar/EventModal.js";
36
- import FloatingActionButton from "../../shared/components/layout/FloatingActionButton.js";
37
- import { EventAnchor, anchorOf } from "../../shared/components/calendar/EventShell.js";
38
- import MiniDatePicker from "@rapidmx/react-shared/components/pickers/MiniDatePicker.js";
39
- import MonthView from "../../shared/components/calendar/MonthView.js";
40
- import SplitDayView from "../../shared/components/calendar/SplitDayView.js";
41
- import TimeGridView from "../../shared/components/calendar/TimeGridView.js";
42
- import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
43
- import Button from "@rapidmx/react-shared/components/buttons/Button.js";
44
- import { SWIPE_PERIOD_SHIFT } from "../../shared/components/calendar/swipeNavigation.js";
45
- import { useSwipeSlide } from "../../shared/gestures/useSwipeSlide.js";
46
- import { useEnterSlide } from "../../shared/gestures/useEnterSlide.js";
47
- import { useWheelPaging, type WheelEdge } from "../../shared/gestures/useWheelPaging.js";
48
- import { SHORTCUTS, ShortcutDef } from "../../shared/keyboard/keymap.js";
49
- import { useShortcut } from "../../shared/keyboard/useShortcut.js";
50
- import { useShortcutProps } from "../../shared/keyboard/useShortcutProps.js";
51
- import { notifyApiError } from "../../shared/notifications/apiErrors.js";
52
- import { bookingSettingsHref } from "../../shared/calendar/bookingPlugin.js";
53
- import { redactedEventUidOf } from "../../shared/calendar/calendarLiveUpdates.js";
54
-
55
- function CalendarPage(props: CalendarShellProps) {
56
- return (
57
- <CalendarShell {...props}>
58
- <CalendarContent userUid={props.userUid} bookingHref={bookingSettingsHref(props.pluginNav)} />
59
- </CalendarShell>
60
- );
61
- }
62
-
63
- type ViewType = "month" | "week" | "workWeek" | "day" | "split";
64
- const VIEW_TYPES: ViewType[] = ["month", "week", "workWeek", "day", "split"];
65
- const VIEW_LABELS: Record<ViewType, string> = { month: "Month", week: "Week", workWeek: "Work Week", day: "Day", split: "Split" };
66
- /** The shortcut that switches to a view (Outlook's Ctrl+Alt+1-4); the split view has none. */
67
- const VIEW_SHORTCUTS: Partial<Record<ViewType, ShortcutDef>> = {
68
- day: SHORTCUTS.calendar.day,
69
- workWeek: SHORTCUTS.calendar.workWeek,
70
- week: SHORTCUTS.calendar.week,
71
- month: SHORTCUTS.calendar.month,
72
- };
73
-
74
- /** One of the view switcher's buttons, with its shortcut in the tooltip. */
75
- function ViewButton({ view, current, onSelect }: { view: ViewType; current: ViewType; onSelect: (view: ViewType) => void }) {
76
- const shortcut = VIEW_SHORTCUTS[view];
77
- const hint = useShortcutProps(VIEW_LABELS[view], shortcut ?? SHORTCUTS.calendar.day, !!shortcut);
78
- return (
79
- <button
80
- type="button"
81
- onClick={() => onSelect(view)}
82
- {...hint}
83
- className={[
84
- "px-3 h-7 text-sm rounded-sm whitespace-nowrap",
85
- current === view ? "bg-primary text-white" : "hover:bg-surface-alt",
86
- (view === "workWeek" || view === "split") && "hidden md:inline-block",
87
- ]
88
- .filter(Boolean)
89
- .join(" ")}
90
- >
91
- {VIEW_LABELS[view]}
92
- </button>
93
- );
94
- }
95
-
96
- interface ModalState {
97
- occurrence: CalendarOccurrence | null;
98
- initialStart?: Date;
99
- initialEnd?: Date;
100
- /** Overrides the default calendar a new event is created into (e.g. the column clicked in Split
101
- * view) — ignored when editing an existing occurrence, which always keeps its own `folderUid`. */
102
- targetFolderUid?: string;
103
- /** What was clicked to start a new event, for the quick-create popover to open beside (none: centered near the top). */
104
- anchor?: EventAnchor;
105
- /** A new event that starts as an all-day one (a day of the month view was clicked). */
106
- initialAllDay?: boolean;
107
- }
108
-
109
- function CalendarContent({ userUid, bookingHref }: { userUid?: string; bookingHref?: string }) {
110
- const { mailboxUid, folderUid, calendarFolders, mailboxCalendars, mailboxes, reloadFolders, colorFor } = useCalendarShell();
111
- // `PointerSensor` alone activates a drag on the very first touch-move, indistinguishable from a
112
- // scroll gesture on a touch device. `MouseSensor` (a small `distance` — desktop drags still start
113
- // immediately on a deliberate movement, no change from before) + `TouchSensor` (a `delay`+`tolerance`
114
- // — a touch drag only activates after a brief press-and-hold, so a quick swipe scrolls normally) is
115
- // dnd-kit's own documented pattern for this exact conflict.
116
- const sensors = useSensors(
117
- useSensor(MouseSensor, { activationConstraint: { distance: 8 } }),
118
- useSensor(TouchSensor, { activationConstraint: { delay: 250, tolerance: 8 } }),
119
- );
120
-
121
- const [drawerOpen, setDrawerOpen] = useState(false);
122
- const [view, setView] = useState<ViewType>("month");
123
- const [viewDate, setViewDate] = useState(() => startOfDay(new Date()));
124
- const [events, setEvents] = useState<CalendarEvent[]>([]);
125
- const [loading, setLoading] = useState(true);
126
- const [error, setError] = useState<string | null>(null);
127
- const [modal, setModal] = useState<ModalState | null>(null);
128
- // True from the moment a touch drag of an event (or its resize handle) activates until it ends or is cancelled.
129
- const [dragging, setDragging] = useState(false);
130
- // `null` means "not yet customized by the user" — every calendar is treated as checked without
131
- // needing to be seeded from `calendarFolders` the moment it loads (see `checkedFolderUids` below).
132
- // Once the user toggles anything, this becomes a real (possibly empty) set of exactly what's checked.
133
- const [checkedFolderUidsState, setCheckedFolderUidsState] = useState<Set<string> | null>(null);
134
-
135
- useEffect(() => {
136
- const params = new URLSearchParams(window.location.search);
137
- const requestedView = params.get("view");
138
- if ((VIEW_TYPES as string[]).includes(requestedView ?? "")) {
139
- setView(requestedView as ViewType);
140
- }
141
- const requestedDate = params.get("date");
142
- // `parseISO()`, not `new Date()` - `?date=2026-06-16` is a local calendar day, but `new Date()` reads a
143
- // date-only string as UTC midnight, i.e. the previous day anywhere west of UTC.
144
- const parsedDate = requestedDate ? parseISO(requestedDate) : null;
145
- if (parsedDate && !isNaN(parsedDate.getTime())) {
146
- setViewDate(startOfDay(parsedDate));
147
- }
148
- }, []);
149
-
150
- const checkedFolderUids = checkedFolderUidsState ?? new Set(calendarFolders.map((f) => f.uid));
151
- const folderColors = useMemo(
152
- () => Object.fromEntries(calendarFolders.map((f) => [f.uid, colorFor(f)])),
153
- [calendarFolders, colorFor],
154
- );
155
-
156
- function toggleCalendar(toggledUid: string) {
157
- setCheckedFolderUidsState((prev) => {
158
- const next = new Set(prev ?? calendarFolders.map((f) => f.uid));
159
- if (next.has(toggledUid)) {
160
- next.delete(toggledUid);
161
- } else {
162
- next.add(toggledUid);
163
- }
164
- return next;
165
- });
166
- }
167
-
168
- async function handleAddCalendar(targetMailboxUid: string, name: string, color: string) {
169
- const created = await createFolder({ mailboxUid: targetMailboxUid, name, type: "calendar", color });
170
- setCheckedFolderUidsState((prev) => new Set([...(prev ?? calendarFolders.map((f) => f.uid)), created.uid]));
171
- reloadFolders();
172
- }
173
-
174
- const { rangeStart, rangeEnd, days } = useMemo(() => {
175
- if (view === "month") {
176
- const start = startOfWeek(startOfMonth(viewDate), { weekStartsOn: 1 });
177
- const end = endOfWeek(endOfMonth(viewDate), { weekStartsOn: 1 });
178
- return { rangeStart: start, rangeEnd: end, days: [] as Date[] };
179
- }
180
- if (view === "week") {
181
- const start = startOfWeek(viewDate, { weekStartsOn: 1 });
182
- const end = endOfWeek(viewDate, { weekStartsOn: 1 });
183
- return { rangeStart: start, rangeEnd: end, days: eachDayOfInterval({ start, end }) };
184
- }
185
- if (view === "workWeek") {
186
- const start = startOfWeek(viewDate, { weekStartsOn: 1 });
187
- const weekdays = eachDayOfInterval({ start, end: endOfWeek(viewDate, { weekStartsOn: 1 }) }).slice(0, 5);
188
- return { rangeStart: start, rangeEnd: endOfDay(weekdays[weekdays.length - 1]), days: weekdays };
189
- }
190
- // "day" and "split" both show a single day, just laid out differently below.
191
- const start = startOfDay(viewDate);
192
- const end = endOfDay(viewDate);
193
- return { rangeStart: start, rangeEnd: end, days: [start] };
194
- }, [view, viewDate]);
195
-
196
- // Only the most recent reload() may apply its results - toggling calendars (or saving/dragging an event)
197
- // while an earlier, slower fetch is still in flight must not let that stale response overwrite newer
198
- // events. (No date range is passed: `listCalendarEvents()` deliberately fetches the whole calendar and
199
- // filters client-side - see its own doc comment on the server-side range query it works around.)
200
- const reloadSeqRef = useRef(0);
201
-
202
- function reload() {
203
- const seq = ++reloadSeqRef.current;
204
- const targets = calendarFolders.filter((f) => checkedFolderUids.has(f.uid));
205
- if (targets.length === 0) {
206
- setEvents([]);
207
- setLoading(false);
208
- return;
209
- }
210
- setLoading(true);
211
- setError(null);
212
- void Promise.allSettled(targets.map((f) => listCalendarEvents(f.uid)))
213
- .then((results) => {
214
- if (seq !== reloadSeqRef.current) {
215
- return;
216
- }
217
- const loaded: CalendarEvent[] = [];
218
- const failures: { name: string; err: unknown }[] = [];
219
- results.forEach((result, i) => {
220
- if (result.status === "fulfilled") {
221
- loaded.push(...result.value);
222
- } else {
223
- failures.push({ name: targets[i].name, err: result.reason });
224
- }
225
- });
226
- setEvents(loaded);
227
- if (failures.length === 0) {
228
- return;
229
- }
230
- // A single calendar's failure (by far the common case — most mailboxes have exactly one)
231
- // surfaces that calendar's own error message unchanged, matching this page's original
232
- // single-calendar behavior exactly; only a genuine multi-calendar partial failure falls
233
- // back to a combined message naming which calendars didn't load.
234
- if (failures.length === 1 && targets.length === 1) {
235
- const err = failures[0].err;
236
- setError(err instanceof ApiRequestError ? err.message : "Could not load your calendar.");
237
- } else {
238
- setError(`Could not load events for: ${failures.map((f) => f.name).join(", ")}.`);
239
- }
240
- })
241
- .finally(() => {
242
- if (seq === reloadSeqRef.current) {
243
- setLoading(false);
244
- }
245
- });
246
- }
247
-
248
- // Keyed on stable string summaries (not the array/set objects themselves, which are new references
249
- // every render) so this only re-fires when the actual set of folders to fetch changes.
250
- const calendarFolderUidsKey = calendarFolders.map((f) => f.uid).sort().join(",");
251
- const checkedFolderUidsKey = Array.from(checkedFolderUids).sort().join(",");
252
- useEffect(reload, [calendarFolderUidsKey, checkedFolderUidsKey]);
253
-
254
- // A private or confidential event that somebody creates or changes is announced on the push connection as its busy block, whatever the reader may
255
- // see (see `redactedEventUidOf()`): that payload is never stored here. The event is fetched again by its uid, which comes back as much as this
256
- // reader may see - all of it for the owner - and replaces (or joins) what the calendar holds; one that is gone (a 404) leaves it. A failed fetch
257
- // changes nothing: the next load shows the event as it is.
258
- const checkedFolderUidsRef = useRef(checkedFolderUids);
259
- checkedFolderUidsRef.current = checkedFolderUids;
260
- useEffect(
261
- () =>
262
- getPushClient().onEvent((event) => {
263
- const uid = redactedEventUidOf(event);
264
- if (!uid) {
265
- return;
266
- }
267
- void getCalendarEvent(uid).then(
268
- (fresh) => {
269
- if (checkedFolderUidsRef.current.has(fresh.folderUid)) {
270
- setEvents((previous) =>
271
- previous.some((existing) => existing.uid === fresh.uid)
272
- ? previous.map((existing) => (existing.uid === fresh.uid ? fresh : existing))
273
- : [...previous, fresh],
274
- );
275
- }
276
- },
277
- (err: unknown) => {
278
- if (err instanceof ApiRequestError && err.status === 404) {
279
- setEvents((previous) => previous.filter((existing) => existing.uid !== uid));
280
- }
281
- },
282
- );
283
- }),
284
- [],
285
- );
286
-
287
- const occurrences = useMemo(
288
- () => expandAllOccurrences(events, rangeStart, rangeEnd),
289
- [events, rangeStart, rangeEnd],
290
- );
291
-
292
- const splitColumns = useMemo(
293
- () =>
294
- calendarFolders
295
- .filter((f) => checkedFolderUids.has(f.uid))
296
- .map((f) => ({ folderUid: f.uid, name: f.name, color: colorFor(f) })),
297
- [calendarFolders, checkedFolderUidsKey, colorFor],
298
- );
299
-
300
- function shiftView(direction: 1 | -1) {
301
- setViewDate((d) =>
302
- view === "month"
303
- ? addMonths(d, direction)
304
- : view === "week" || view === "workWeek"
305
- ? addWeeks(d, direction)
306
- : addDays(d, direction),
307
- );
308
- }
309
-
310
- // The phone layout's swipe between periods. Only the view area carries it (not the toolbar), it calls the same `shiftView()`
311
- // the Previous/Next buttons and shortcuts do, and it is off while an event is being dragged or the editor is open. The views
312
- // never scroll sideways (their columns share the width), so there is no horizontal scroller for the gesture to fight with.
313
- // The view follows the finger and, once the swipe commits, slides out while the next period slides in (see `useSwipeSlide()`).
314
- const isMobile = useIsMobile();
315
- const swipe = useSwipeSlide({
316
- enabled: isMobile && !dragging && !modal,
317
- onShift: (direction) => shiftView(SWIPE_PERIOD_SHIFT[direction]),
318
- });
319
-
320
- // The desktop's mouse wheel steps through the months without end; in the views that scroll through the hours of a day it scrolls
321
- // them as usual and, pushed past the last (or first) hour, carries on into the next (or previous) day, week or work week. Each
322
- // step is the same `shiftView()` and enters with a short vertical slide (`useEnterSlide()`), which never waits for the last one.
323
- const wheelSlide = useEnterSlide();
324
- /** Where the scroller of the page a wheel step arrived at starts: its top after going on, its bottom after going back. */
325
- const arrivalEdge = useRef<WheelEdge | null>(null);
326
- const scrollerOf = (): HTMLElement | null => swipe.ref.current?.querySelector<HTMLElement>("[data-calendar-scroller]") ?? null;
327
- useWheelPaging(swipe.ref, {
328
- enabled: !isMobile && !dragging && !modal,
329
- getScroller: () => (view === "month" ? null : scrollerOf()),
330
- onStep: (direction, edge) => {
331
- arrivalEdge.current = edge;
332
- shiftView(direction);
333
- wheelSlide.enter(direction);
334
- },
335
- });
336
- useLayoutEffect(() => {
337
- const edge = arrivalEdge.current;
338
- arrivalEdge.current = null;
339
- const scroller = edge ? scrollerOf() : null;
340
- if (scroller) {
341
- scroller.scrollTop = edge === "top" ? 0 : scroller.scrollHeight;
342
- }
343
- }, [viewDate]);
344
-
345
- function goToday() {
346
- setViewDate(startOfDay(new Date()));
347
- }
348
-
349
- function handleSelectDay(date: Date) {
350
- setView("day");
351
- setViewDate(startOfDay(date));
352
- }
353
-
354
- function openNewEvent(start?: Date, end?: Date, targetFolderUid?: string, anchor?: EventAnchor, initialAllDay?: boolean) {
355
- if (!mailboxUid || !folderUid) {
356
- return;
357
- }
358
- setModal({
359
- occurrence: null,
360
- initialStart: start ?? new Date(),
361
- initialEnd: end ?? new Date(Date.now() + 30 * 60_000),
362
- targetFolderUid,
363
- anchor,
364
- initialAllDay,
365
- });
366
- }
367
-
368
- function openEvent(occurrence: CalendarOccurrence) {
369
- setModal({ occurrence });
370
- }
371
-
372
- function closeModal() {
373
- setModal(null);
374
- }
375
-
376
- // Keyboard shortcuts - the toolbar's own actions. (A dialog open - the event editor - silences them.)
377
- useShortcut(SHORTCUTS.calendar.create, () => openNewEvent(), { enabled: !!mailboxUid && !!folderUid });
378
- useShortcut(SHORTCUTS.calendar.today, goToday);
379
- useShortcut(SHORTCUTS.calendar.previous, () => shiftView(-1));
380
- useShortcut(SHORTCUTS.calendar.next, () => shiftView(1));
381
- useShortcut(SHORTCUTS.calendar.day, () => setView("day"));
382
- useShortcut(SHORTCUTS.calendar.workWeek, () => setView("workWeek"));
383
- useShortcut(SHORTCUTS.calendar.week, () => setView("week"));
384
- useShortcut(SHORTCUTS.calendar.month, () => setView("month"));
385
- const newEventHint = useShortcutProps("New event", SHORTCUTS.calendar.create, !!mailboxUid && !!folderUid);
386
- const previousHint = useShortcutProps("Previous", SHORTCUTS.calendar.previous);
387
- const todayHint = useShortcutProps("Today", SHORTCUTS.calendar.today);
388
- const nextHint = useShortcutProps("Next", SHORTCUTS.calendar.next);
389
-
390
- function handleSaved() {
391
- setModal(null);
392
- reload();
393
- }
394
-
395
- function handleDeleted() {
396
- setModal(null);
397
- reload();
398
- }
399
-
400
- async function handleDragEnd(event: DragEndEvent) {
401
- setDragging(false);
402
- const overId = event.over ? String(event.over.id) : undefined;
403
- const action = resolveDragAction(String(event.active.id), overId, occurrences);
404
- if (!action) {
405
- return;
406
- }
407
- try {
408
- if (action.type === "move") {
409
- await moveOccurrence(action.occurrence, action.deltaMs);
410
- } else {
411
- await resizeOccurrenceEnd(action.occurrence, action.newEnd);
412
- }
413
- reload();
414
- } catch (err) {
415
- notifyApiError(err, action.type === "move" ? "Couldn't move the event" : "Couldn't resize the event");
416
- }
417
- }
418
-
419
- const title =
420
- view === "month"
421
- ? format(viewDate, "MMMM yyyy")
422
- : view === "week" || view === "workWeek"
423
- ? `${format(rangeStart, "MMM d")} – ${format(rangeEnd, "MMM d, yyyy")}`
424
- : format(viewDate, "EEEE, MMMM d, yyyy");
425
-
426
- const modalFolderUid = modal ? (modal.targetFolderUid ?? modal.occurrence?.folderUid ?? folderUid) : undefined;
427
- const modalMailboxUid = calendarFolders.find((f) => f.uid === modalFolderUid)?.mailboxUid ?? mailboxUid;
428
- const modalMailbox = mailboxes.find((mb) => mb.uid === modalMailboxUid);
429
- // Mailboxes a new event can be created in - only those with at least one calendar that the caller can
430
- // write to (a view-only share is left out; see writableMailboxes.ts).
431
- const writableMailboxes = useWritableMailboxes(mailboxes, userUid, modalMailboxUid);
432
- const mailboxOptions = useMemo(
433
- () =>
434
- mailboxCalendars
435
- .filter((mc) => mc.calendarFolders.length > 0 && writableMailboxes.some((mb) => mb.uid === mc.mailbox.uid))
436
- .map((mc) => ({ mailbox: mc.mailbox, calendars: mc.calendarFolders.map((f) => ({ uid: f.uid, name: f.name })) })),
437
- [mailboxCalendars, writableMailboxes],
438
- );
439
-
440
- const sidebarContent = (
441
- <>
442
- <MiniDatePicker selected={viewDate} onSelect={(date) => setViewDate(startOfDay(date))} />
443
- <CalendarListSidebar
444
- mailboxCalendars={mailboxCalendars}
445
- checkedFolderUids={checkedFolderUids}
446
- onToggle={toggleCalendar}
447
- onAddCalendar={handleAddCalendar}
448
- colorFor={colorFor}
449
- />
450
- </>
451
- );
452
-
453
- return (
454
- <div className="flex-1 min-w-0 flex min-h-0">
455
- <div className="hidden lg:flex w-56 shrink-0 border-r border-border flex-col overflow-y-auto">{sidebarContent}</div>
456
- <Drawer open={drawerOpen} onClose={() => setDrawerOpen(false)} title="Calendars" fullScreen>
457
- <div className="flex flex-col">{sidebarContent}</div>
458
- </Drawer>
459
- {/* `min-w-0`: without it the column is as wide as its widest row (the toolbar, the month grid) and runs off the window instead of the row wrapping. */}
460
- <div className="flex-1 min-w-0 flex flex-col min-h-0">
461
- <div className="flex flex-wrap items-center gap-x-3 gap-y-2 p-3 border-b border-border shrink-0">
462
- <button
463
- type="button"
464
- className="lg:hidden w-9 h-9 flex items-center justify-center rounded-sm text-text-muted hover:bg-surface-alt hover:text-text shrink-0"
465
- aria-label="Open calendars"
466
- onClick={() => setDrawerOpen(true)}
467
- >
468
- <HiOutlineBars3 size={20} aria-hidden="true" />
469
- </button>
470
- {/* On a phone New event is the floating button at the bottom of the page instead, as in Mail. */}
471
- {!isMobile && (
472
- <Button type="button" onClick={(e) => openNewEvent(undefined, undefined, undefined, anchorOf(e.currentTarget, "below"))} className="!w-auto shrink-0" {...newEventHint}>
473
- + New event
474
- </Button>
475
- )}
476
- <div className="flex items-center gap-1">
477
- <button type="button" onClick={() => shiftView(-1)} aria-label="Previous" {...previousHint} className="w-7 h-7 text-sm rounded-sm hover:bg-surface-alt">
478
- &lsaquo;
479
- </button>
480
- <button type="button" onClick={goToday} {...todayHint} className="px-2 h-7 text-sm rounded-sm hover:bg-surface-alt">
481
- Today
482
- </button>
483
- <button type="button" onClick={() => shiftView(1)} aria-label="Next" {...nextHint} className="w-7 h-7 text-sm rounded-sm hover:bg-surface-alt">
484
- &rsaquo;
485
- </button>
486
- </div>
487
- <h1 className="hidden md:block text-lg font-bold tracking-tight">{title}</h1>
488
- <div className="ml-auto flex flex-wrap gap-1">
489
- {VIEW_TYPES.map((v) => (
490
- <ViewButton key={v} view={v} current={view} onSelect={setView} />
491
- ))}
492
- </div>
493
- </div>
494
-
495
- {error && (
496
- <div className="p-3">
497
- <Alert>{error}</Alert>
498
- </div>
499
- )}
500
-
501
- <div ref={swipe.ref} className="flex-1 min-w-0 flex flex-col min-h-0 overflow-hidden" {...swipe.handlers} style={swipe.style}>
502
- <div data-swipe-phase={swipe.phase} className="flex-1 min-w-0 flex flex-col min-h-0" style={swipe.contentStyle}>
503
- <div className="flex-1 min-w-0 flex flex-col min-h-0" style={wheelSlide.style}>
504
- {loading ? (
505
- <p className="p-4 text-sm text-text-muted">Loading&hellip;</p>
506
- ) : (
507
- <DndContext
508
- sensors={sensors}
509
- onDragStart={() => setDragging(true)}
510
- onDragEnd={handleDragEnd}
511
- onDragCancel={() => setDragging(false)}
512
- >
513
- {view === "month" ? (
514
- <MonthView
515
- viewDate={viewDate}
516
- occurrences={occurrences}
517
- folderColors={folderColors}
518
- onSelectDay={handleSelectDay}
519
- onSelectEvent={openEvent}
520
- onSelectSlot={(start, end, anchor) => openNewEvent(start, end, undefined, anchor, true)}
521
- />
522
- ) : view === "split" ? (
523
- <SplitDayView
524
- day={viewDate}
525
- columns={splitColumns}
526
- occurrences={occurrences}
527
- onSelectEvent={openEvent}
528
- onSelectSlot={(start, end, targetFolderUid, anchor) => openNewEvent(start, end, targetFolderUid, anchor)}
529
- />
530
- ) : (
531
- <TimeGridView
532
- days={days}
533
- occurrences={occurrences}
534
- folderColors={folderColors}
535
- onSelectEvent={openEvent}
536
- onSelectSlot={(start, end, anchor) => openNewEvent(start, end, undefined, anchor)}
537
- />
538
- )}
539
- </DndContext>
540
- )}
541
- </div>
542
- </div>
543
- </div>
544
-
545
- {/* `folderUid` is guaranteed defined whenever `modal` is: `openNewEvent` only sets it after
546
- checking it, and `openEvent` only fires from an occurrence that itself required a
547
- successful, folder-scoped load to render. The modal is scoped to that folder's own mailbox
548
- (which may be a shared one) - its organizer address and calendar picker come from there. */}
549
- {modal && modalMailbox && (
550
- <EventModal
551
- open
552
- onClose={closeModal}
553
- mailboxUid={modalMailbox.uid}
554
- folderUid={modalFolderUid!}
555
- calendars={calendarFolders.filter((f) => f.mailboxUid === modalMailbox.uid).map((f) => ({ uid: f.uid, name: f.name }))}
556
- mailboxOptions={mailboxOptions}
557
- folderColors={folderColors}
558
- organizerAddress={modalMailbox.primarySmtpAddress}
559
- organizerAliases={modalMailbox.aliasAddresses}
560
- occurrence={modal.occurrence}
561
- initialStart={modal.initialStart}
562
- initialEnd={modal.initialEnd}
563
- initialAllDay={modal.initialAllDay}
564
- anchor={modal.anchor}
565
- bookingHref={bookingHref}
566
- onSaved={handleSaved}
567
- onDeleted={handleDeleted}
568
- />
569
- )}
570
- {/* The phone layout's New event: the old toolbar button's action (its quick form is a bottom sheet there, so no anchor), offered
571
- only where that button could act - a calendar to put the event in - and not while the editor or its sheet is open. */}
572
- {isMobile && mailboxUid && folderUid && !modal && <FloatingActionButton label="New event" icon={HiOutlinePlus} onClick={() => openNewEvent()} />}
573
- </div>
574
- </div>
575
- );
576
- }
577
-
578
- export default CalendarPage;
579
-
580
- /** The tab's title: `Brand: Calendar` (see `pageTitle()`). */
581
- export const title = pageTitle("Calendar");
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { pageTitle } from "../../shared/navigation/pageTitle.js";
6
+ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
7
+ import { DndContext, DragEndEvent, MouseSensor, TouchSensor, useSensor, useSensors } from "@dnd-kit/core";
8
+ import { HiOutlineBars3, HiOutlinePlus } from "react-icons/hi2";
9
+ import {
10
+ addDays,
11
+ addMonths,
12
+ addWeeks,
13
+ eachDayOfInterval,
14
+ endOfDay,
15
+ endOfMonth,
16
+ endOfWeek,
17
+ format,
18
+ parseISO,
19
+ startOfDay,
20
+ startOfMonth,
21
+ startOfWeek,
22
+ } from "date-fns";
23
+ import { ApiRequestError } from "../../../lib/util/api.js";
24
+ import { useApiClient } from "../../../lib/util/apiClientContext.js";
25
+ import useIsMobile from "../../../lib/util/useIsMobile.js";
26
+ import { CalendarEvent, getCalendarEvent, listCalendarEvents } from "../../../lib/calendar/calendarApi.js";
27
+ import { getPushClient } from "../../../lib/mail/pushClient.js";
28
+ import { moveOccurrence, resizeOccurrenceEnd } from "../../../lib/calendar/calendarMutations.js";
29
+ import { resolveDragAction } from "../../../lib/calendar/calendarDragIds.js";
30
+ import { createFolder } from "../../../lib/mail/mailApi.js";
31
+ import { CalendarOccurrence, expandAllOccurrences } from "../../../lib/calendar/recurrence.js";
32
+ import Drawer from "../../../lib/components/overlays/Drawer.js";
33
+ import CalendarShell, { CalendarShellProps, useCalendarShell } from "../../shared/components/calendar/layout/CalendarShell.js";
34
+ import CalendarListSidebar from "../../shared/components/calendar/CalendarListSidebar.js";
35
+ import { useWritableMailboxes } from "../../shared/components/mail/writableMailboxes.js";
36
+ import EventModal from "../../shared/components/calendar/EventModal.js";
37
+ import FloatingActionButton from "../../shared/components/layout/FloatingActionButton.js";
38
+ import { EventAnchor, anchorOf } from "../../shared/components/calendar/EventShell.js";
39
+ import MiniDatePicker from "../../../lib/components/pickers/MiniDatePicker.js";
40
+ import MonthView from "../../shared/components/calendar/MonthView.js";
41
+ import SplitDayView from "../../shared/components/calendar/SplitDayView.js";
42
+ import TimeGridView from "../../shared/components/calendar/TimeGridView.js";
43
+ import Alert from "../../../lib/components/feedback/Alert.js";
44
+ import Button from "../../../lib/components/buttons/Button.js";
45
+ import { SWIPE_PERIOD_SHIFT } from "../../shared/components/calendar/swipeNavigation.js";
46
+ import { useSwipeSlide } from "../../shared/gestures/useSwipeSlide.js";
47
+ import { useEnterSlide } from "../../shared/gestures/useEnterSlide.js";
48
+ import { useWheelPaging, type WheelEdge } from "../../shared/gestures/useWheelPaging.js";
49
+ import { SHORTCUTS, ShortcutDef } from "../../shared/keyboard/keymap.js";
50
+ import { useShortcut } from "../../shared/keyboard/useShortcut.js";
51
+ import { useShortcutProps } from "../../shared/keyboard/useShortcutProps.js";
52
+ import { notifyApiError } from "../../shared/notifications/apiErrors.js";
53
+ import { bookingSettingsHref } from "../../shared/calendar/bookingPlugin.js";
54
+ import { redactedEventUidOf } from "../../shared/calendar/calendarLiveUpdates.js";
55
+
56
+ function CalendarPage(props: CalendarShellProps) {
57
+ return (
58
+ <CalendarShell {...props}>
59
+ <CalendarContent userUid={props.userUid} bookingHref={bookingSettingsHref(props.pluginNav)} />
60
+ </CalendarShell>
61
+ );
62
+ }
63
+
64
+ type ViewType = "month" | "week" | "workWeek" | "day" | "split";
65
+ const VIEW_TYPES: ViewType[] = ["month", "week", "workWeek", "day", "split"];
66
+ const VIEW_LABELS: Record<ViewType, string> = { month: "Month", week: "Week", workWeek: "Work Week", day: "Day", split: "Split" };
67
+ /** The shortcut that switches to a view (Outlook's Ctrl+Alt+1-4); the split view has none. */
68
+ const VIEW_SHORTCUTS: Partial<Record<ViewType, ShortcutDef>> = {
69
+ day: SHORTCUTS.calendar.day,
70
+ workWeek: SHORTCUTS.calendar.workWeek,
71
+ week: SHORTCUTS.calendar.week,
72
+ month: SHORTCUTS.calendar.month,
73
+ };
74
+
75
+ /** One of the view switcher's buttons, with its shortcut in the tooltip. */
76
+ function ViewButton({ view, current, onSelect }: { view: ViewType; current: ViewType; onSelect: (view: ViewType) => void }) {
77
+ const shortcut = VIEW_SHORTCUTS[view];
78
+ const hint = useShortcutProps(VIEW_LABELS[view], shortcut ?? SHORTCUTS.calendar.day, !!shortcut);
79
+ return (
80
+ <button
81
+ type="button"
82
+ onClick={() => onSelect(view)}
83
+ {...hint}
84
+ className={[
85
+ "px-3 h-7 text-sm rounded-sm whitespace-nowrap",
86
+ current === view ? "bg-primary text-white" : "hover:bg-surface-alt",
87
+ (view === "workWeek" || view === "split") && "hidden md:inline-block",
88
+ ]
89
+ .filter(Boolean)
90
+ .join(" ")}
91
+ >
92
+ {VIEW_LABELS[view]}
93
+ </button>
94
+ );
95
+ }
96
+
97
+ interface ModalState {
98
+ occurrence: CalendarOccurrence | null;
99
+ initialStart?: Date;
100
+ initialEnd?: Date;
101
+ /** Overrides the default calendar a new event is created into (e.g. the column clicked in Split
102
+ * view) — ignored when editing an existing occurrence, which always keeps its own `folderUid`. */
103
+ targetFolderUid?: string;
104
+ /** What was clicked to start a new event, for the quick-create popover to open beside (none: centered near the top). */
105
+ anchor?: EventAnchor;
106
+ /** A new event that starts as an all-day one (a day of the month view was clicked). */
107
+ initialAllDay?: boolean;
108
+ }
109
+
110
+ function CalendarContent({ userUid, bookingHref }: { userUid?: string; bookingHref?: string }) {
111
+ const { mailboxUid, folderUid, calendarFolders, mailboxCalendars, mailboxes, reloadFolders, colorFor } = useCalendarShell();
112
+ const client = useApiClient();
113
+ // `PointerSensor` alone activates a drag on the very first touch-move, indistinguishable from a
114
+ // scroll gesture on a touch device. `MouseSensor` (a small `distance` — desktop drags still start
115
+ // immediately on a deliberate movement, no change from before) + `TouchSensor` (a `delay`+`tolerance`
116
+ // — a touch drag only activates after a brief press-and-hold, so a quick swipe scrolls normally) is
117
+ // dnd-kit's own documented pattern for this exact conflict.
118
+ const sensors = useSensors(
119
+ useSensor(MouseSensor, { activationConstraint: { distance: 8 } }),
120
+ useSensor(TouchSensor, { activationConstraint: { delay: 250, tolerance: 8 } }),
121
+ );
122
+
123
+ const [drawerOpen, setDrawerOpen] = useState(false);
124
+ const [view, setView] = useState<ViewType>("month");
125
+ const [viewDate, setViewDate] = useState(() => startOfDay(new Date()));
126
+ const [events, setEvents] = useState<CalendarEvent[]>([]);
127
+ const [loading, setLoading] = useState(true);
128
+ const [error, setError] = useState<string | null>(null);
129
+ const [modal, setModal] = useState<ModalState | null>(null);
130
+ // True from the moment a touch drag of an event (or its resize handle) activates until it ends or is cancelled.
131
+ const [dragging, setDragging] = useState(false);
132
+ // `null` means "not yet customized by the user" — every calendar is treated as checked without
133
+ // needing to be seeded from `calendarFolders` the moment it loads (see `checkedFolderUids` below).
134
+ // Once the user toggles anything, this becomes a real (possibly empty) set of exactly what's checked.
135
+ const [checkedFolderUidsState, setCheckedFolderUidsState] = useState<Set<string> | null>(null);
136
+
137
+ useEffect(() => {
138
+ const params = new URLSearchParams(window.location.search);
139
+ const requestedView = params.get("view");
140
+ if ((VIEW_TYPES as string[]).includes(requestedView ?? "")) {
141
+ setView(requestedView as ViewType);
142
+ }
143
+ const requestedDate = params.get("date");
144
+ // `parseISO()`, not `new Date()` - `?date=2026-06-16` is a local calendar day, but `new Date()` reads a
145
+ // date-only string as UTC midnight, i.e. the previous day anywhere west of UTC.
146
+ const parsedDate = requestedDate ? parseISO(requestedDate) : null;
147
+ if (parsedDate && !isNaN(parsedDate.getTime())) {
148
+ setViewDate(startOfDay(parsedDate));
149
+ }
150
+ }, []);
151
+
152
+ const checkedFolderUids = checkedFolderUidsState ?? new Set(calendarFolders.map((f) => f.uid));
153
+ const folderColors = useMemo(
154
+ () => Object.fromEntries(calendarFolders.map((f) => [f.uid, colorFor(f)])),
155
+ [calendarFolders, colorFor],
156
+ );
157
+
158
+ function toggleCalendar(toggledUid: string) {
159
+ setCheckedFolderUidsState((prev) => {
160
+ const next = new Set(prev ?? calendarFolders.map((f) => f.uid));
161
+ if (next.has(toggledUid)) {
162
+ next.delete(toggledUid);
163
+ } else {
164
+ next.add(toggledUid);
165
+ }
166
+ return next;
167
+ });
168
+ }
169
+
170
+ async function handleAddCalendar(targetMailboxUid: string, name: string, color: string) {
171
+ const created = await createFolder({ mailboxUid: targetMailboxUid, name, type: "calendar", color }, client);
172
+ setCheckedFolderUidsState((prev) => new Set([...(prev ?? calendarFolders.map((f) => f.uid)), created.uid]));
173
+ reloadFolders();
174
+ }
175
+
176
+ const { rangeStart, rangeEnd, days } = useMemo(() => {
177
+ if (view === "month") {
178
+ const start = startOfWeek(startOfMonth(viewDate), { weekStartsOn: 1 });
179
+ const end = endOfWeek(endOfMonth(viewDate), { weekStartsOn: 1 });
180
+ return { rangeStart: start, rangeEnd: end, days: [] as Date[] };
181
+ }
182
+ if (view === "week") {
183
+ const start = startOfWeek(viewDate, { weekStartsOn: 1 });
184
+ const end = endOfWeek(viewDate, { weekStartsOn: 1 });
185
+ return { rangeStart: start, rangeEnd: end, days: eachDayOfInterval({ start, end }) };
186
+ }
187
+ if (view === "workWeek") {
188
+ const start = startOfWeek(viewDate, { weekStartsOn: 1 });
189
+ const weekdays = eachDayOfInterval({ start, end: endOfWeek(viewDate, { weekStartsOn: 1 }) }).slice(0, 5);
190
+ return { rangeStart: start, rangeEnd: endOfDay(weekdays[weekdays.length - 1]), days: weekdays };
191
+ }
192
+ // "day" and "split" both show a single day, just laid out differently below.
193
+ const start = startOfDay(viewDate);
194
+ const end = endOfDay(viewDate);
195
+ return { rangeStart: start, rangeEnd: end, days: [start] };
196
+ }, [view, viewDate]);
197
+
198
+ // Only the most recent reload() may apply its results - toggling calendars (or saving/dragging an event)
199
+ // while an earlier, slower fetch is still in flight must not let that stale response overwrite newer
200
+ // events. (No date range is passed: `listCalendarEvents()` deliberately fetches the whole calendar and
201
+ // filters client-side - see its own doc comment on the server-side range query it works around.)
202
+ const reloadSeqRef = useRef(0);
203
+
204
+ function reload() {
205
+ const seq = ++reloadSeqRef.current;
206
+ const targets = calendarFolders.filter((f) => checkedFolderUids.has(f.uid));
207
+ if (targets.length === 0) {
208
+ setEvents([]);
209
+ setLoading(false);
210
+ return;
211
+ }
212
+ setLoading(true);
213
+ setError(null);
214
+ void Promise.allSettled(targets.map((f) => listCalendarEvents(f.uid, client)))
215
+ .then((results) => {
216
+ if (seq !== reloadSeqRef.current) {
217
+ return;
218
+ }
219
+ const loaded: CalendarEvent[] = [];
220
+ const failures: { name: string; err: unknown }[] = [];
221
+ results.forEach((result, i) => {
222
+ if (result.status === "fulfilled") {
223
+ loaded.push(...result.value);
224
+ } else {
225
+ failures.push({ name: targets[i].name, err: result.reason });
226
+ }
227
+ });
228
+ setEvents(loaded);
229
+ if (failures.length === 0) {
230
+ return;
231
+ }
232
+ // A single calendar's failure (by far the common case — most mailboxes have exactly one)
233
+ // surfaces that calendar's own error message unchanged, matching this page's original
234
+ // single-calendar behavior exactly; only a genuine multi-calendar partial failure falls
235
+ // back to a combined message naming which calendars didn't load.
236
+ if (failures.length === 1 && targets.length === 1) {
237
+ const err = failures[0].err;
238
+ setError(err instanceof ApiRequestError ? err.message : "Could not load your calendar.");
239
+ } else {
240
+ setError(`Could not load events for: ${failures.map((f) => f.name).join(", ")}.`);
241
+ }
242
+ })
243
+ .finally(() => {
244
+ if (seq === reloadSeqRef.current) {
245
+ setLoading(false);
246
+ }
247
+ });
248
+ }
249
+
250
+ // Keyed on stable string summaries (not the array/set objects themselves, which are new references
251
+ // every render) so this only re-fires when the actual set of folders to fetch changes.
252
+ const calendarFolderUidsKey = calendarFolders.map((f) => f.uid).sort().join(",");
253
+ const checkedFolderUidsKey = Array.from(checkedFolderUids).sort().join(",");
254
+ useEffect(reload, [calendarFolderUidsKey, checkedFolderUidsKey]);
255
+
256
+ // A private or confidential event that somebody creates or changes is announced on the push connection as its busy block, whatever the reader may
257
+ // see (see `redactedEventUidOf()`): that payload is never stored here. The event is fetched again by its uid, which comes back as much as this
258
+ // reader may see - all of it for the owner - and replaces (or joins) what the calendar holds; one that is gone (a 404) leaves it. A failed fetch
259
+ // changes nothing: the next load shows the event as it is.
260
+ const checkedFolderUidsRef = useRef(checkedFolderUids);
261
+ checkedFolderUidsRef.current = checkedFolderUids;
262
+ useEffect(
263
+ () =>
264
+ getPushClient().onEvent((event) => {
265
+ const uid = redactedEventUidOf(event);
266
+ if (!uid) {
267
+ return;
268
+ }
269
+ void getCalendarEvent(uid, client).then(
270
+ (fresh) => {
271
+ if (checkedFolderUidsRef.current.has(fresh.folderUid)) {
272
+ setEvents((previous) =>
273
+ previous.some((existing) => existing.uid === fresh.uid)
274
+ ? previous.map((existing) => (existing.uid === fresh.uid ? fresh : existing))
275
+ : [...previous, fresh],
276
+ );
277
+ }
278
+ },
279
+ (err: unknown) => {
280
+ if (err instanceof ApiRequestError && err.status === 404) {
281
+ setEvents((previous) => previous.filter((existing) => existing.uid !== uid));
282
+ }
283
+ },
284
+ );
285
+ }),
286
+ [client],
287
+ );
288
+
289
+ const occurrences = useMemo(
290
+ () => expandAllOccurrences(events, rangeStart, rangeEnd),
291
+ [events, rangeStart, rangeEnd],
292
+ );
293
+
294
+ const splitColumns = useMemo(
295
+ () =>
296
+ calendarFolders
297
+ .filter((f) => checkedFolderUids.has(f.uid))
298
+ .map((f) => ({ folderUid: f.uid, name: f.name, color: colorFor(f) })),
299
+ [calendarFolders, checkedFolderUidsKey, colorFor],
300
+ );
301
+
302
+ function shiftView(direction: 1 | -1) {
303
+ setViewDate((d) =>
304
+ view === "month"
305
+ ? addMonths(d, direction)
306
+ : view === "week" || view === "workWeek"
307
+ ? addWeeks(d, direction)
308
+ : addDays(d, direction),
309
+ );
310
+ }
311
+
312
+ // The phone layout's swipe between periods. Only the view area carries it (not the toolbar), it calls the same `shiftView()`
313
+ // the Previous/Next buttons and shortcuts do, and it is off while an event is being dragged or the editor is open. The views
314
+ // never scroll sideways (their columns share the width), so there is no horizontal scroller for the gesture to fight with.
315
+ // The view follows the finger and, once the swipe commits, slides out while the next period slides in (see `useSwipeSlide()`).
316
+ const isMobile = useIsMobile();
317
+ const swipe = useSwipeSlide({
318
+ enabled: isMobile && !dragging && !modal,
319
+ onShift: (direction) => shiftView(SWIPE_PERIOD_SHIFT[direction]),
320
+ });
321
+
322
+ // The desktop's mouse wheel steps through the months without end; in the views that scroll through the hours of a day it scrolls
323
+ // them as usual and, pushed past the last (or first) hour, carries on into the next (or previous) day, week or work week. Each
324
+ // step is the same `shiftView()` and enters with a short vertical slide (`useEnterSlide()`), which never waits for the last one.
325
+ const wheelSlide = useEnterSlide();
326
+ /** Where the scroller of the page a wheel step arrived at starts: its top after going on, its bottom after going back. */
327
+ const arrivalEdge = useRef<WheelEdge | null>(null);
328
+ const scrollerOf = (): HTMLElement | null => swipe.ref.current?.querySelector<HTMLElement>("[data-calendar-scroller]") ?? null;
329
+ useWheelPaging(swipe.ref, {
330
+ enabled: !isMobile && !dragging && !modal,
331
+ getScroller: () => (view === "month" ? null : scrollerOf()),
332
+ onStep: (direction, edge) => {
333
+ arrivalEdge.current = edge;
334
+ shiftView(direction);
335
+ wheelSlide.enter(direction);
336
+ },
337
+ });
338
+ useLayoutEffect(() => {
339
+ const edge = arrivalEdge.current;
340
+ arrivalEdge.current = null;
341
+ const scroller = edge ? scrollerOf() : null;
342
+ if (scroller) {
343
+ scroller.scrollTop = edge === "top" ? 0 : scroller.scrollHeight;
344
+ }
345
+ }, [viewDate]);
346
+
347
+ function goToday() {
348
+ setViewDate(startOfDay(new Date()));
349
+ }
350
+
351
+ function handleSelectDay(date: Date) {
352
+ setView("day");
353
+ setViewDate(startOfDay(date));
354
+ }
355
+
356
+ function openNewEvent(start?: Date, end?: Date, targetFolderUid?: string, anchor?: EventAnchor, initialAllDay?: boolean) {
357
+ if (!mailboxUid || !folderUid) {
358
+ return;
359
+ }
360
+ setModal({
361
+ occurrence: null,
362
+ initialStart: start ?? new Date(),
363
+ initialEnd: end ?? new Date(Date.now() + 30 * 60_000),
364
+ targetFolderUid,
365
+ anchor,
366
+ initialAllDay,
367
+ });
368
+ }
369
+
370
+ function openEvent(occurrence: CalendarOccurrence) {
371
+ setModal({ occurrence });
372
+ }
373
+
374
+ function closeModal() {
375
+ setModal(null);
376
+ }
377
+
378
+ // Keyboard shortcuts - the toolbar's own actions. (A dialog open - the event editor - silences them.)
379
+ useShortcut(SHORTCUTS.calendar.create, () => openNewEvent(), { enabled: !!mailboxUid && !!folderUid });
380
+ useShortcut(SHORTCUTS.calendar.today, goToday);
381
+ useShortcut(SHORTCUTS.calendar.previous, () => shiftView(-1));
382
+ useShortcut(SHORTCUTS.calendar.next, () => shiftView(1));
383
+ useShortcut(SHORTCUTS.calendar.day, () => setView("day"));
384
+ useShortcut(SHORTCUTS.calendar.workWeek, () => setView("workWeek"));
385
+ useShortcut(SHORTCUTS.calendar.week, () => setView("week"));
386
+ useShortcut(SHORTCUTS.calendar.month, () => setView("month"));
387
+ const newEventHint = useShortcutProps("New event", SHORTCUTS.calendar.create, !!mailboxUid && !!folderUid);
388
+ const previousHint = useShortcutProps("Previous", SHORTCUTS.calendar.previous);
389
+ const todayHint = useShortcutProps("Today", SHORTCUTS.calendar.today);
390
+ const nextHint = useShortcutProps("Next", SHORTCUTS.calendar.next);
391
+
392
+ function handleSaved() {
393
+ setModal(null);
394
+ reload();
395
+ }
396
+
397
+ function handleDeleted() {
398
+ setModal(null);
399
+ reload();
400
+ }
401
+
402
+ async function handleDragEnd(event: DragEndEvent) {
403
+ setDragging(false);
404
+ const overId = event.over ? String(event.over.id) : undefined;
405
+ const action = resolveDragAction(String(event.active.id), overId, occurrences);
406
+ if (!action) {
407
+ return;
408
+ }
409
+ try {
410
+ if (action.type === "move") {
411
+ await moveOccurrence(action.occurrence, action.deltaMs, client);
412
+ } else {
413
+ await resizeOccurrenceEnd(action.occurrence, action.newEnd, client);
414
+ }
415
+ reload();
416
+ } catch (err) {
417
+ notifyApiError(err, action.type === "move" ? "Couldn't move the event" : "Couldn't resize the event");
418
+ }
419
+ }
420
+
421
+ const title =
422
+ view === "month"
423
+ ? format(viewDate, "MMMM yyyy")
424
+ : view === "week" || view === "workWeek"
425
+ ? `${format(rangeStart, "MMM d")} – ${format(rangeEnd, "MMM d, yyyy")}`
426
+ : format(viewDate, "EEEE, MMMM d, yyyy");
427
+
428
+ const modalFolderUid = modal ? (modal.targetFolderUid ?? modal.occurrence?.folderUid ?? folderUid) : undefined;
429
+ const modalMailboxUid = calendarFolders.find((f) => f.uid === modalFolderUid)?.mailboxUid ?? mailboxUid;
430
+ const modalMailbox = mailboxes.find((mb) => mb.uid === modalMailboxUid);
431
+ // Mailboxes a new event can be created in - only those with at least one calendar that the caller can
432
+ // write to (a view-only share is left out; see writableMailboxes.ts).
433
+ const writableMailboxes = useWritableMailboxes(mailboxes, userUid, modalMailboxUid);
434
+ const mailboxOptions = useMemo(
435
+ () =>
436
+ mailboxCalendars
437
+ .filter((mc) => mc.calendarFolders.length > 0 && writableMailboxes.some((mb) => mb.uid === mc.mailbox.uid))
438
+ .map((mc) => ({ mailbox: mc.mailbox, calendars: mc.calendarFolders.map((f) => ({ uid: f.uid, name: f.name })) })),
439
+ [mailboxCalendars, writableMailboxes],
440
+ );
441
+
442
+ const sidebarContent = (
443
+ <>
444
+ <MiniDatePicker selected={viewDate} onSelect={(date) => setViewDate(startOfDay(date))} />
445
+ <CalendarListSidebar
446
+ mailboxCalendars={mailboxCalendars}
447
+ checkedFolderUids={checkedFolderUids}
448
+ onToggle={toggleCalendar}
449
+ onAddCalendar={handleAddCalendar}
450
+ colorFor={colorFor}
451
+ />
452
+ </>
453
+ );
454
+
455
+ return (
456
+ <div className="flex-1 min-w-0 flex min-h-0">
457
+ <div className="hidden lg:flex w-56 shrink-0 border-r border-border flex-col overflow-y-auto">{sidebarContent}</div>
458
+ <Drawer open={drawerOpen} onClose={() => setDrawerOpen(false)} title="Calendars" fullScreen>
459
+ <div className="flex flex-col">{sidebarContent}</div>
460
+ </Drawer>
461
+ {/* `min-w-0`: without it the column is as wide as its widest row (the toolbar, the month grid) and runs off the window instead of the row wrapping. */}
462
+ <div className="flex-1 min-w-0 flex flex-col min-h-0">
463
+ <div className="flex flex-wrap items-center gap-x-3 gap-y-2 p-3 border-b border-border shrink-0">
464
+ <button
465
+ type="button"
466
+ className="lg:hidden w-9 h-9 flex items-center justify-center rounded-sm text-text-muted hover:bg-surface-alt hover:text-text shrink-0"
467
+ aria-label="Open calendars"
468
+ onClick={() => setDrawerOpen(true)}
469
+ >
470
+ <HiOutlineBars3 size={20} aria-hidden="true" />
471
+ </button>
472
+ {/* On a phone New event is the floating button at the bottom of the page instead, as in Mail. */}
473
+ {!isMobile && (
474
+ <Button type="button" onClick={(e) => openNewEvent(undefined, undefined, undefined, anchorOf(e.currentTarget, "below"))} className="!w-auto shrink-0" {...newEventHint}>
475
+ + New event
476
+ </Button>
477
+ )}
478
+ <div className="flex items-center gap-1">
479
+ <button type="button" onClick={() => shiftView(-1)} aria-label="Previous" {...previousHint} className="w-7 h-7 text-sm rounded-sm hover:bg-surface-alt">
480
+ &lsaquo;
481
+ </button>
482
+ <button type="button" onClick={goToday} {...todayHint} className="px-2 h-7 text-sm rounded-sm hover:bg-surface-alt">
483
+ Today
484
+ </button>
485
+ <button type="button" onClick={() => shiftView(1)} aria-label="Next" {...nextHint} className="w-7 h-7 text-sm rounded-sm hover:bg-surface-alt">
486
+ &rsaquo;
487
+ </button>
488
+ </div>
489
+ <h1 className="hidden md:block text-lg font-bold tracking-tight">{title}</h1>
490
+ <div className="ml-auto flex flex-wrap gap-1">
491
+ {VIEW_TYPES.map((v) => (
492
+ <ViewButton key={v} view={v} current={view} onSelect={setView} />
493
+ ))}
494
+ </div>
495
+ </div>
496
+
497
+ {error && (
498
+ <div className="p-3">
499
+ <Alert>{error}</Alert>
500
+ </div>
501
+ )}
502
+
503
+ <div ref={swipe.ref} className="flex-1 min-w-0 flex flex-col min-h-0 overflow-hidden" {...swipe.handlers} style={swipe.style}>
504
+ <div data-swipe-phase={swipe.phase} className="flex-1 min-w-0 flex flex-col min-h-0" style={swipe.contentStyle}>
505
+ <div className="flex-1 min-w-0 flex flex-col min-h-0" style={wheelSlide.style}>
506
+ {loading ? (
507
+ <p className="p-4 text-sm text-text-muted">Loading&hellip;</p>
508
+ ) : (
509
+ <DndContext
510
+ sensors={sensors}
511
+ onDragStart={() => setDragging(true)}
512
+ onDragEnd={handleDragEnd}
513
+ onDragCancel={() => setDragging(false)}
514
+ >
515
+ {view === "month" ? (
516
+ <MonthView
517
+ viewDate={viewDate}
518
+ occurrences={occurrences}
519
+ folderColors={folderColors}
520
+ onSelectDay={handleSelectDay}
521
+ onSelectEvent={openEvent}
522
+ onSelectSlot={(start, end, anchor) => openNewEvent(start, end, undefined, anchor, true)}
523
+ />
524
+ ) : view === "split" ? (
525
+ <SplitDayView
526
+ day={viewDate}
527
+ columns={splitColumns}
528
+ occurrences={occurrences}
529
+ onSelectEvent={openEvent}
530
+ onSelectSlot={(start, end, targetFolderUid, anchor) => openNewEvent(start, end, targetFolderUid, anchor)}
531
+ />
532
+ ) : (
533
+ <TimeGridView
534
+ days={days}
535
+ occurrences={occurrences}
536
+ folderColors={folderColors}
537
+ onSelectEvent={openEvent}
538
+ onSelectSlot={(start, end, anchor) => openNewEvent(start, end, undefined, anchor)}
539
+ />
540
+ )}
541
+ </DndContext>
542
+ )}
543
+ </div>
544
+ </div>
545
+ </div>
546
+
547
+ {/* `folderUid` is guaranteed defined whenever `modal` is: `openNewEvent` only sets it after
548
+ checking it, and `openEvent` only fires from an occurrence that itself required a
549
+ successful, folder-scoped load to render. The modal is scoped to that folder's own mailbox
550
+ (which may be a shared one) - its organizer address and calendar picker come from there. */}
551
+ {modal && modalMailbox && (
552
+ <EventModal
553
+ open
554
+ onClose={closeModal}
555
+ mailboxUid={modalMailbox.uid}
556
+ folderUid={modalFolderUid!}
557
+ calendars={calendarFolders.filter((f) => f.mailboxUid === modalMailbox.uid).map((f) => ({ uid: f.uid, name: f.name }))}
558
+ mailboxOptions={mailboxOptions}
559
+ folderColors={folderColors}
560
+ organizerAddress={modalMailbox.primarySmtpAddress}
561
+ organizerAliases={modalMailbox.aliasAddresses}
562
+ occurrence={modal.occurrence}
563
+ initialStart={modal.initialStart}
564
+ initialEnd={modal.initialEnd}
565
+ initialAllDay={modal.initialAllDay}
566
+ anchor={modal.anchor}
567
+ bookingHref={bookingHref}
568
+ onSaved={handleSaved}
569
+ onDeleted={handleDeleted}
570
+ />
571
+ )}
572
+ {/* The phone layout's New event: the old toolbar button's action (its quick form is a bottom sheet there, so no anchor), offered
573
+ only where that button could act - a calendar to put the event in - and not while the editor or its sheet is open. */}
574
+ {isMobile && mailboxUid && folderUid && !modal && <FloatingActionButton label="New event" icon={HiOutlinePlus} onClick={() => openNewEvent()} />}
575
+ </div>
576
+ </div>
577
+ );
578
+ }
579
+
580
+ export default CalendarPage;
581
+
582
+ /** The tab's title: `Brand: Calendar` (see `pageTitle()`). */
583
+ export const title = pageTitle("Calendar");