@rapidmx/web-client 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +373 -88
- package/apps/admin/_layout.tsx +8 -2
- package/apps/admin/index.tsx +7 -1
- package/apps/admin/ingest-queue/index.tsx +2 -1
- package/apps/admin/mailboxes/[uid].tsx +23 -10
- package/apps/admin/quarantine/index.tsx +2 -1
- package/apps/admin/signing-certificates/index.tsx +343 -0
- package/apps/escrow/_layout.tsx +8 -2
- package/apps/shared/appearance/AppearanceForm.tsx +392 -0
- package/apps/shared/appearance/AppearanceHead.tsx +49 -0
- package/apps/shared/appearance/AppearanceProvider.tsx +470 -0
- package/apps/shared/appearance/ColorField.tsx +107 -0
- package/apps/shared/appearance/appearanceCache.ts +106 -0
- package/apps/shared/appearance/appearanceContext.ts +77 -0
- package/apps/shared/appearance/bootScript.ts +35 -0
- package/apps/shared/appearance/color.ts +169 -0
- package/apps/shared/appearance/photo.ts +88 -0
- package/apps/shared/appearance/resolvedTheme.ts +67 -0
- package/apps/shared/appearance/theme.ts +355 -0
- package/apps/shared/auth/accountUrl.ts +9 -0
- package/apps/shared/auth/adminAccess.ts +99 -0
- package/apps/shared/components/admin/layout/AdminShell.tsx +22 -10
- package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +107 -60
- package/apps/shared/components/admin/settings/BrandingForm.tsx +24 -0
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +2 -1
- package/apps/shared/components/admin/settings/PluginsManager.tsx +228 -25
- package/apps/shared/components/admin/setup/SetupWizard.tsx +2 -1
- package/apps/shared/components/calendar/CalendarListSidebar.tsx +4 -5
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +8 -2
- package/apps/shared/components/contacts/ContactForm.tsx +11 -5
- package/apps/shared/components/contacts/ContactsSidebar.tsx +5 -3
- package/apps/shared/components/contacts/ContactsToolbar.tsx +98 -115
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +25 -8
- package/apps/shared/components/escrow/layout/EscrowShell.tsx +13 -6
- package/apps/shared/components/layout/AppShell.tsx +477 -288
- package/apps/shared/components/layout/BrandingChrome.tsx +200 -13
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -389
- package/apps/shared/components/layout/MailboxProvisioning.tsx +168 -164
- package/apps/shared/components/layout/RailIcon.tsx +100 -0
- package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -0
- package/apps/shared/components/layout/ThemeSwitch.tsx +84 -0
- package/apps/shared/components/layout/UserMenu.tsx +372 -185
- package/apps/shared/components/mail/ConversationList.tsx +271 -262
- package/apps/shared/components/mail/ConversationThreadPane.tsx +147 -94
- package/apps/shared/components/mail/LazyReadingPane.tsx +113 -0
- package/apps/shared/components/mail/MailSelectionBar.tsx +227 -222
- package/apps/shared/components/mail/MessageDetailPane.tsx +1537 -1387
- package/apps/shared/components/mail/OutboxBadge.tsx +44 -0
- package/apps/shared/components/mail/OutboxRowStatus.tsx +31 -0
- package/apps/shared/components/mail/compose/ComposeContext.tsx +312 -160
- package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -418
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +1644 -1736
- package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +111 -0
- package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -118
- package/apps/shared/components/mail/compose/composePerf.ts +46 -0
- package/apps/shared/components/mail/compose/encryptionRequirement.ts +86 -0
- package/apps/shared/components/mail/compose/quotedBody.ts +161 -100
- package/apps/shared/components/mail/layout/MailShell.tsx +490 -472
- package/apps/shared/components/mail/reading/EncryptedBody.tsx +65 -0
- package/apps/shared/components/mail/reading/EncryptedPreview.tsx +35 -0
- package/apps/shared/components/mail/reading/MessageBody.tsx +240 -0
- package/apps/shared/components/mail/reading/MessageCard.tsx +174 -0
- package/apps/shared/components/mail/reading/bodyContent.ts +100 -0
- package/apps/shared/components/mail/reading/bodyHtml.ts +298 -0
- package/apps/shared/components/mail/reading/color.ts +245 -0
- package/apps/shared/components/mail/reading/frameControl.ts +142 -0
- package/apps/shared/components/mail/reading/frameDocument.ts +123 -0
- package/apps/shared/components/mail/reading/safeDocument.ts +24 -0
- package/apps/shared/components/mail/reading/themeAdaptation.ts +221 -0
- package/apps/shared/components/mail/reading/themeSurface.ts +90 -0
- package/apps/shared/components/mail/reading/viewOriginal.ts +38 -0
- package/apps/shared/components/mail/unreadStyle.tsx +75 -0
- package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -0
- package/apps/shared/components/settings/layout/SettingsShell.tsx +253 -240
- package/apps/shared/components/sharing/PrincipalPicker.tsx +145 -0
- package/apps/shared/components/tasks/TasksSidebar.tsx +5 -3
- package/apps/shared/components/tasks/layout/TasksShell.tsx +24 -8
- package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -0
- package/apps/shared/keyboard/ShortcutProvider.tsx +62 -0
- package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -0
- package/apps/shared/keyboard/dispatch.ts +124 -0
- package/apps/shared/keyboard/format.ts +89 -0
- package/apps/shared/keyboard/keymap.ts +114 -0
- package/apps/shared/keyboard/match.ts +44 -0
- package/apps/shared/keyboard/parse.ts +136 -0
- package/apps/shared/keyboard/platform.ts +34 -0
- package/apps/shared/keyboard/registry.ts +65 -0
- package/apps/shared/keyboard/targets.ts +79 -0
- package/apps/shared/keyboard/useShortcut.ts +50 -0
- package/apps/shared/keyboard/useShortcutProps.ts +17 -0
- package/apps/shared/mail/folderCounts.ts +308 -0
- package/apps/shared/mail/folderTree.ts +143 -0
- package/apps/shared/mail/listSnapshots.ts +87 -0
- package/apps/shared/mail/messageReadState.ts +85 -0
- package/apps/shared/mail/newMailNotifications.ts +183 -0
- package/apps/shared/mail/outbox/UnlockBridge.tsx +23 -0
- package/apps/shared/mail/outbox/composeBridge.ts +69 -0
- package/apps/shared/mail/outbox/outboxState.ts +88 -0
- package/apps/shared/mail/outbox/pendingSends.ts +146 -0
- package/apps/shared/mail/outbox/sendDecision.ts +157 -0
- package/apps/shared/mail/outbox/sendJob.ts +427 -0
- package/apps/shared/mail/outbox/sendOutcomes.ts +146 -0
- package/apps/shared/mail/outbox/sendState.ts +35 -0
- package/apps/shared/mail/outbox/useOutboxStatus.ts +61 -0
- package/apps/shared/mail/useMailConnection.ts +203 -0
- package/apps/shared/mail/useMailLiveUpdates.ts +277 -224
- package/apps/shared/mail/useMarkMessageRead.ts +47 -0
- package/apps/shared/mail/useNewMailNotifications.ts +178 -0
- package/apps/shared/mail/useUnreadTitle.ts +42 -0
- package/apps/shared/navigation/AppRouter.tsx +300 -0
- package/apps/shared/navigation/appHrefs.ts +23 -0
- package/apps/shared/navigation/frameContext.tsx +35 -0
- package/apps/shared/navigation/idle.ts +45 -0
- package/apps/shared/navigation/routerContext.tsx +83 -0
- package/apps/shared/navigation/routes.ts +72 -0
- package/apps/shared/notifications/NotificationCenter.tsx +215 -0
- package/apps/shared/notifications/NotificationHistoryDialog.tsx +90 -0
- package/apps/shared/notifications/apiErrors.ts +98 -0
- package/apps/shared/notifications/headerOffset.ts +35 -0
- package/apps/shared/notifications/pushStatus.ts +56 -0
- package/apps/shared/notifications/store.ts +549 -0
- package/apps/shared/notifications/systemErrors.ts +63 -0
- package/apps/shared/notifications/useNotifications.ts +41 -0
- package/apps/shared/search/LocalIndexLifecycle.tsx +114 -98
- package/apps/shared/signing/enrollmentStorage.ts +33 -0
- package/apps/shared/signing/enrollmentTracker.ts +385 -0
- package/apps/shared/signing/enrollmentView.ts +251 -0
- package/apps/shared/signing/signingInfo.ts +58 -0
- package/apps/shared/signing/useNow.ts +19 -0
- package/apps/shared/signing/useSigningEnrollmentWatcher.ts +90 -0
- package/apps/shared/styles/app.css +108 -10
- package/apps/www/_layout.tsx +8 -2
- package/apps/www/_routedPage.tsx +24 -0
- package/apps/www/_routes.ts +36 -0
- package/apps/www/calendar/index.tsx +64 -33
- package/apps/www/contacts/[uid].tsx +109 -107
- package/apps/www/contacts/index.tsx +62 -42
- package/apps/www/index.tsx +2291 -1917
- package/apps/www/messages/[uid].tsx +108 -101
- package/apps/www/settings/appearance/index.tsx +21 -0
- package/apps/www/settings/auto-reply/index.tsx +7 -8
- package/apps/www/settings/encryption/index.tsx +1287 -1249
- package/apps/www/settings/filters/[uid].tsx +7 -2
- package/apps/www/settings/filters/index.tsx +102 -99
- package/apps/www/settings/filters/new/index.tsx +140 -133
- package/apps/www/settings/labels/index.tsx +204 -201
- package/apps/www/settings/privacy/index.tsx +7 -9
- package/apps/www/settings/read-receipts/index.tsx +7 -8
- package/apps/www/settings/sharing/index.tsx +256 -274
- package/apps/www/settings/signatures/[uid].tsx +172 -167
- package/apps/www/settings/signatures/index.tsx +88 -85
- package/apps/www/settings/signatures/new/index.tsx +135 -129
- package/apps/www/tasks/index.tsx +29 -18
- package/dist/apps/admin/_layout.d.ts +5 -1
- package/dist/apps/admin/_layout.js +3 -2
- package/dist/apps/admin/index.js +3 -2
- package/dist/apps/admin/ingest-queue/index.js +2 -1
- package/dist/apps/admin/mailboxes/[uid].js +9 -6
- package/dist/apps/admin/quarantine/index.js +2 -1
- package/dist/apps/admin/signing-certificates/index.d.ts +3 -0
- package/dist/apps/admin/signing-certificates/index.js +161 -0
- package/dist/apps/escrow/_layout.d.ts +5 -1
- package/dist/apps/escrow/_layout.js +3 -2
- package/dist/apps/shared/appearance/AppearanceForm.d.ts +7 -0
- package/dist/apps/shared/appearance/AppearanceForm.js +131 -0
- package/dist/apps/shared/appearance/AppearanceHead.d.ts +15 -0
- package/dist/apps/shared/appearance/AppearanceHead.js +31 -0
- package/dist/apps/shared/appearance/AppearanceProvider.d.ts +32 -0
- package/dist/apps/shared/appearance/AppearanceProvider.js +390 -0
- package/dist/apps/shared/appearance/ColorField.d.ts +22 -0
- package/dist/apps/shared/appearance/ColorField.js +40 -0
- package/dist/apps/shared/appearance/appearanceCache.d.ts +45 -0
- package/dist/apps/shared/appearance/appearanceCache.js +80 -0
- package/dist/apps/shared/appearance/appearanceContext.d.ts +45 -0
- package/dist/apps/shared/appearance/appearanceContext.js +29 -0
- package/dist/apps/shared/appearance/bootScript.d.ts +16 -0
- package/dist/apps/shared/appearance/bootScript.js +33 -0
- package/dist/apps/shared/appearance/color.d.ts +65 -0
- package/dist/apps/shared/appearance/color.js +132 -0
- package/dist/apps/shared/appearance/photo.d.ts +17 -0
- package/dist/apps/shared/appearance/photo.js +79 -0
- package/dist/apps/shared/appearance/resolvedTheme.d.ts +21 -0
- package/dist/apps/shared/appearance/resolvedTheme.js +55 -0
- package/dist/apps/shared/appearance/theme.d.ts +111 -0
- package/dist/apps/shared/appearance/theme.js +251 -0
- package/dist/apps/shared/auth/accountUrl.d.ts +2 -0
- package/dist/apps/shared/auth/accountUrl.js +8 -0
- package/dist/apps/shared/auth/adminAccess.d.ts +30 -0
- package/dist/apps/shared/auth/adminAccess.js +89 -0
- package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
- package/dist/apps/shared/components/admin/layout/AdminShell.js +16 -6
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.d.ts +10 -5
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +49 -34
- package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +2 -0
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +7 -1
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +1 -1
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +106 -20
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +2 -1
- package/dist/apps/shared/components/calendar/CalendarListSidebar.js +8 -8
- package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +8 -4
- package/dist/apps/shared/components/contacts/ContactForm.js +1 -1
- package/dist/apps/shared/components/contacts/ContactsSidebar.js +4 -2
- package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +6 -2
- package/dist/apps/shared/components/contacts/ContactsToolbar.js +28 -10
- package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
- package/dist/apps/shared/components/contacts/layout/ContactsShell.js +19 -6
- package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -4
- package/dist/apps/shared/components/layout/AppShell.d.ts +34 -4
- package/dist/apps/shared/components/layout/AppShell.js +122 -17
- package/dist/apps/shared/components/layout/BrandingChrome.d.ts +69 -10
- package/dist/apps/shared/components/layout/BrandingChrome.js +140 -12
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +10 -9
- package/dist/apps/shared/components/layout/MailboxProvisioning.js +3 -1
- package/dist/apps/shared/components/layout/RailIcon.d.ts +21 -0
- package/dist/apps/shared/components/layout/RailIcon.js +85 -0
- package/dist/apps/shared/components/layout/ResponsiveToolbar.d.ts +55 -0
- package/dist/apps/shared/components/layout/ResponsiveToolbar.js +199 -0
- package/dist/apps/shared/components/layout/ThemeSwitch.d.ts +9 -0
- package/dist/apps/shared/components/layout/ThemeSwitch.js +54 -0
- package/dist/apps/shared/components/layout/UserMenu.d.ts +25 -3
- package/dist/apps/shared/components/layout/UserMenu.js +88 -7
- package/dist/apps/shared/components/mail/ConversationList.js +5 -11
- package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +6 -2
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +87 -40
- package/dist/apps/shared/components/mail/LazyReadingPane.d.ts +9 -0
- package/dist/apps/shared/components/mail/LazyReadingPane.js +83 -0
- package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +6 -4
- package/dist/apps/shared/components/mail/MailSelectionBar.js +11 -4
- package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +25 -8
- package/dist/apps/shared/components/mail/MessageDetailPane.js +167 -73
- package/dist/apps/shared/components/mail/OutboxBadge.d.ts +17 -0
- package/dist/apps/shared/components/mail/OutboxBadge.js +18 -0
- package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +10 -0
- package/dist/apps/shared/components/mail/OutboxRowStatus.js +19 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +38 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +97 -6
- package/dist/apps/shared/components/mail/compose/ComposeToolbar.js +4 -3
- package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +3 -2
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +316 -339
- package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.d.ts +19 -0
- package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +33 -0
- package/dist/apps/shared/components/mail/compose/RichTextEditor.d.ts +9 -1
- package/dist/apps/shared/components/mail/compose/RichTextEditor.js +20 -2
- package/dist/apps/shared/components/mail/compose/composePerf.d.ts +16 -0
- package/dist/apps/shared/components/mail/compose/composePerf.js +41 -0
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +49 -0
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +35 -0
- package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +13 -0
- package/dist/apps/shared/components/mail/compose/quotedBody.js +67 -11
- package/dist/apps/shared/components/mail/layout/MailShell.d.ts +14 -4
- package/dist/apps/shared/components/mail/layout/MailShell.js +90 -104
- package/dist/apps/shared/components/mail/reading/EncryptedBody.d.ts +21 -0
- package/dist/apps/shared/components/mail/reading/EncryptedBody.js +22 -0
- package/dist/apps/shared/components/mail/reading/EncryptedPreview.d.ts +18 -0
- package/dist/apps/shared/components/mail/reading/EncryptedPreview.js +22 -0
- package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +32 -0
- package/dist/apps/shared/components/mail/reading/MessageBody.js +136 -0
- package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +62 -0
- package/dist/apps/shared/components/mail/reading/MessageCard.js +58 -0
- package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +38 -0
- package/dist/apps/shared/components/mail/reading/bodyContent.js +84 -0
- package/dist/apps/shared/components/mail/reading/bodyHtml.d.ts +47 -0
- package/dist/apps/shared/components/mail/reading/bodyHtml.js +267 -0
- package/dist/apps/shared/components/mail/reading/color.d.ts +63 -0
- package/dist/apps/shared/components/mail/reading/color.js +201 -0
- package/dist/apps/shared/components/mail/reading/frameControl.d.ts +33 -0
- package/dist/apps/shared/components/mail/reading/frameControl.js +120 -0
- package/dist/apps/shared/components/mail/reading/frameDocument.d.ts +52 -0
- package/dist/apps/shared/components/mail/reading/frameDocument.js +98 -0
- package/dist/apps/shared/components/mail/reading/safeDocument.d.ts +3 -0
- package/dist/apps/shared/components/mail/reading/safeDocument.js +12 -0
- package/dist/apps/shared/components/mail/reading/themeAdaptation.d.ts +56 -0
- package/dist/apps/shared/components/mail/reading/themeAdaptation.js +144 -0
- package/dist/apps/shared/components/mail/reading/themeSurface.d.ts +17 -0
- package/dist/apps/shared/components/mail/reading/themeSurface.js +83 -0
- package/dist/apps/shared/components/mail/reading/viewOriginal.d.ts +9 -0
- package/dist/apps/shared/components/mail/reading/viewOriginal.js +32 -0
- package/dist/apps/shared/components/mail/unreadStyle.d.ts +45 -0
- package/dist/apps/shared/components/mail/unreadStyle.js +61 -0
- package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +32 -0
- package/dist/apps/shared/components/settings/SigningCertificateCard.js +98 -0
- package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +16 -5
- package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +26 -0
- package/dist/apps/shared/components/sharing/PrincipalPicker.js +77 -0
- package/dist/apps/shared/components/tasks/TasksSidebar.js +4 -2
- package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +18 -6
- package/dist/apps/shared/keyboard/GlobalShortcuts.d.ts +14 -0
- package/dist/apps/shared/keyboard/GlobalShortcuts.js +37 -0
- package/dist/apps/shared/keyboard/ShortcutProvider.d.ts +20 -0
- package/dist/apps/shared/keyboard/ShortcutProvider.js +52 -0
- package/dist/apps/shared/keyboard/ShortcutsDialog.d.ts +14 -0
- package/dist/apps/shared/keyboard/ShortcutsDialog.js +42 -0
- package/dist/apps/shared/keyboard/dispatch.d.ts +16 -0
- package/dist/apps/shared/keyboard/dispatch.js +109 -0
- package/dist/apps/shared/keyboard/format.d.ts +12 -0
- package/dist/apps/shared/keyboard/format.js +74 -0
- package/dist/apps/shared/keyboard/keymap.d.ts +294 -0
- package/dist/apps/shared/keyboard/keymap.js +84 -0
- package/dist/apps/shared/keyboard/match.d.ts +20 -0
- package/dist/apps/shared/keyboard/match.js +31 -0
- package/dist/apps/shared/keyboard/parse.d.ts +31 -0
- package/dist/apps/shared/keyboard/parse.js +107 -0
- package/dist/apps/shared/keyboard/platform.d.ts +15 -0
- package/dist/apps/shared/keyboard/platform.js +22 -0
- package/dist/apps/shared/keyboard/registry.d.ts +39 -0
- package/dist/apps/shared/keyboard/registry.js +33 -0
- package/dist/apps/shared/keyboard/targets.d.ts +14 -0
- package/dist/apps/shared/keyboard/targets.js +67 -0
- package/dist/apps/shared/keyboard/useShortcut.d.ts +19 -0
- package/dist/apps/shared/keyboard/useShortcut.js +35 -0
- package/dist/apps/shared/keyboard/useShortcutProps.d.ts +10 -0
- package/dist/apps/shared/keyboard/useShortcutProps.js +15 -0
- package/dist/apps/shared/mail/folderCounts.d.ts +78 -0
- package/dist/apps/shared/mail/folderCounts.js +218 -0
- package/dist/apps/shared/mail/folderTree.d.ts +55 -0
- package/dist/apps/shared/mail/folderTree.js +116 -0
- package/dist/apps/shared/mail/listSnapshots.d.ts +46 -0
- package/dist/apps/shared/mail/listSnapshots.js +43 -0
- package/dist/apps/shared/mail/messageReadState.d.ts +32 -0
- package/dist/apps/shared/mail/messageReadState.js +63 -0
- package/dist/apps/shared/mail/newMailNotifications.d.ts +62 -0
- package/dist/apps/shared/mail/newMailNotifications.js +138 -0
- package/dist/apps/shared/mail/outbox/UnlockBridge.d.ts +5 -0
- package/dist/apps/shared/mail/outbox/UnlockBridge.js +18 -0
- package/dist/apps/shared/mail/outbox/composeBridge.d.ts +34 -0
- package/dist/apps/shared/mail/outbox/composeBridge.js +32 -0
- package/dist/apps/shared/mail/outbox/outboxState.d.ts +41 -0
- package/dist/apps/shared/mail/outbox/outboxState.js +42 -0
- package/dist/apps/shared/mail/outbox/pendingSends.d.ts +39 -0
- package/dist/apps/shared/mail/outbox/pendingSends.js +109 -0
- package/dist/apps/shared/mail/outbox/sendDecision.d.ts +80 -0
- package/dist/apps/shared/mail/outbox/sendDecision.js +94 -0
- package/dist/apps/shared/mail/outbox/sendJob.d.ts +68 -0
- package/dist/apps/shared/mail/outbox/sendJob.js +319 -0
- package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +3 -0
- package/dist/apps/shared/mail/outbox/sendOutcomes.js +141 -0
- package/dist/apps/shared/mail/outbox/sendState.d.ts +25 -0
- package/dist/apps/shared/mail/outbox/sendState.js +8 -0
- package/dist/apps/shared/mail/outbox/useOutboxStatus.d.ts +11 -0
- package/dist/apps/shared/mail/outbox/useOutboxStatus.js +49 -0
- package/dist/apps/shared/mail/useMailConnection.d.ts +56 -0
- package/dist/apps/shared/mail/useMailConnection.js +138 -0
- package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +29 -9
- package/dist/apps/shared/mail/useMailLiveUpdates.js +60 -32
- package/dist/apps/shared/mail/useMarkMessageRead.d.ts +12 -0
- package/dist/apps/shared/mail/useMarkMessageRead.js +44 -0
- package/dist/apps/shared/mail/useNewMailNotifications.d.ts +35 -0
- package/dist/apps/shared/mail/useNewMailNotifications.js +130 -0
- package/dist/apps/shared/mail/useUnreadTitle.d.ts +18 -0
- package/dist/apps/shared/mail/useUnreadTitle.js +31 -0
- package/dist/apps/shared/navigation/AppRouter.d.ts +52 -0
- package/dist/apps/shared/navigation/AppRouter.js +242 -0
- package/dist/apps/shared/navigation/appHrefs.d.ts +14 -0
- package/dist/apps/shared/navigation/appHrefs.js +20 -0
- package/dist/apps/shared/navigation/frameContext.d.ts +19 -0
- package/dist/apps/shared/navigation/frameContext.js +24 -0
- package/dist/apps/shared/navigation/idle.d.ts +14 -0
- package/dist/apps/shared/navigation/idle.js +43 -0
- package/dist/apps/shared/navigation/routerContext.d.ts +37 -0
- package/dist/apps/shared/navigation/routerContext.js +56 -0
- package/dist/apps/shared/navigation/routes.d.ts +32 -0
- package/dist/apps/shared/navigation/routes.js +37 -0
- package/dist/apps/shared/notifications/NotificationCenter.d.ts +19 -0
- package/dist/apps/shared/notifications/NotificationCenter.js +90 -0
- package/dist/apps/shared/notifications/NotificationHistoryDialog.d.ts +10 -0
- package/dist/apps/shared/notifications/NotificationHistoryDialog.js +36 -0
- package/dist/apps/shared/notifications/apiErrors.d.ts +19 -0
- package/dist/apps/shared/notifications/apiErrors.js +86 -0
- package/dist/apps/shared/notifications/headerOffset.d.ts +8 -0
- package/dist/apps/shared/notifications/headerOffset.js +33 -0
- package/dist/apps/shared/notifications/pushStatus.d.ts +9 -0
- package/dist/apps/shared/notifications/pushStatus.js +54 -0
- package/dist/apps/shared/notifications/store.d.ts +136 -0
- package/dist/apps/shared/notifications/store.js +416 -0
- package/dist/apps/shared/notifications/systemErrors.d.ts +9 -0
- package/dist/apps/shared/notifications/systemErrors.js +58 -0
- package/dist/apps/shared/notifications/useNotifications.d.ts +17 -0
- package/dist/apps/shared/notifications/useNotifications.js +18 -0
- package/dist/apps/shared/search/LocalIndexLifecycle.js +17 -3
- package/dist/apps/shared/signing/enrollmentStorage.d.ts +10 -0
- package/dist/apps/shared/signing/enrollmentStorage.js +33 -0
- package/dist/apps/shared/signing/enrollmentTracker.d.ts +73 -0
- package/dist/apps/shared/signing/enrollmentTracker.js +307 -0
- package/dist/apps/shared/signing/enrollmentView.d.ts +57 -0
- package/dist/apps/shared/signing/enrollmentView.js +214 -0
- package/dist/apps/shared/signing/signingInfo.d.ts +11 -0
- package/dist/apps/shared/signing/signingInfo.js +48 -0
- package/dist/apps/shared/signing/useNow.d.ts +2 -0
- package/dist/apps/shared/signing/useNow.js +18 -0
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +16 -0
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +87 -0
- package/dist/apps/shared/styles/app.css +108 -10
- package/dist/apps/www/_layout.d.ts +5 -1
- package/dist/apps/www/_layout.js +3 -2
- package/dist/apps/www/_routedPage.d.ts +12 -0
- package/dist/apps/www/_routedPage.js +19 -0
- package/dist/apps/www/_routes.d.ts +11 -0
- package/dist/apps/www/_routes.js +30 -0
- package/dist/apps/www/calendar/index.d.ts +2 -2
- package/dist/apps/www/calendar/index.js +41 -11
- package/dist/apps/www/contacts/[uid].d.ts +3 -9
- package/dist/apps/www/contacts/[uid].js +11 -9
- package/dist/apps/www/contacts/index.d.ts +2 -2
- package/dist/apps/www/contacts/index.js +30 -24
- package/dist/apps/www/index.d.ts +2 -2
- package/dist/apps/www/index.js +332 -48
- package/dist/apps/www/messages/[uid].d.ts +3 -7
- package/dist/apps/www/messages/[uid].js +10 -5
- package/dist/apps/www/settings/appearance/index.d.ts +4 -0
- package/dist/apps/www/settings/appearance/index.js +13 -0
- package/dist/apps/www/settings/auto-reply/index.d.ts +2 -2
- package/dist/apps/www/settings/auto-reply/index.js +6 -7
- package/dist/apps/www/settings/encryption/index.d.ts +2 -2
- package/dist/apps/www/settings/encryption/index.js +106 -96
- package/dist/apps/www/settings/filters/[uid].d.ts +2 -2
- package/dist/apps/www/settings/filters/[uid].js +6 -2
- package/dist/apps/www/settings/filters/index.d.ts +2 -2
- package/dist/apps/www/settings/filters/index.js +3 -1
- package/dist/apps/www/settings/filters/new/index.d.ts +2 -2
- package/dist/apps/www/settings/filters/new/index.js +9 -3
- package/dist/apps/www/settings/labels/index.d.ts +2 -2
- package/dist/apps/www/settings/labels/index.js +3 -1
- package/dist/apps/www/settings/privacy/index.d.ts +2 -2
- package/dist/apps/www/settings/privacy/index.js +8 -9
- package/dist/apps/www/settings/read-receipts/index.d.ts +2 -2
- package/dist/apps/www/settings/read-receipts/index.js +6 -7
- package/dist/apps/www/settings/sharing/index.d.ts +2 -2
- package/dist/apps/www/settings/sharing/index.js +22 -34
- package/dist/apps/www/settings/signatures/[uid].d.ts +2 -2
- package/dist/apps/www/settings/signatures/[uid].js +6 -2
- package/dist/apps/www/settings/signatures/index.d.ts +2 -2
- package/dist/apps/www/settings/signatures/index.js +3 -1
- package/dist/apps/www/settings/signatures/new/index.d.ts +2 -2
- package/dist/apps/www/settings/signatures/new/index.js +9 -4
- package/dist/apps/www/tasks/index.d.ts +2 -2
- package/dist/apps/www/tasks/index.js +22 -16
- package/package.json +2 -2
- package/apps/shared/components/mail/compose/SendFailureAlert.tsx +0 -48
- package/dist/apps/shared/components/mail/compose/SendFailureAlert.d.ts +0 -14
- package/dist/apps/shared/components/mail/compose/SendFailureAlert.js +0 -11
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
6
6
|
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
7
|
-
import { Attachment, Folder, Message, listAttachments
|
|
7
|
+
import { Attachment, Folder, Message, listAttachments } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
8
8
|
import { ConversationSummary, listConversationMessages } from "@rapidmx/react-shared/mail/conversationsApi.js";
|
|
9
9
|
import { Label } from "@rapidmx/react-shared/mail/labelsApi.js";
|
|
10
10
|
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
11
|
-
import MailAddress from "./MailAddress.js";
|
|
12
11
|
import MessageDetailPane from "./MessageDetailPane.js";
|
|
12
|
+
import { EncryptedPreview, displaySubject } from "./reading/EncryptedPreview.js";
|
|
13
|
+
import { CollapsedCard, SkeletonCards, SubjectCard } from "./reading/MessageCard.js";
|
|
14
|
+
import { useMailShell } from "./layout/MailShell.js";
|
|
15
|
+
import { setReadState } from "../../mail/messageReadState.js";
|
|
16
|
+
import { dateClass, isUnread, senderClass } from "./unreadStyle.js";
|
|
13
17
|
|
|
14
18
|
/** One request's worth of the thread. The server's own default for `listConversationMessages()`. */
|
|
15
19
|
export const THREAD_PAGE_SIZE = 100;
|
|
@@ -51,6 +55,9 @@ export interface ConversationThreadPaneProps {
|
|
|
51
55
|
onLabelCreated?: (label: Label) => void;
|
|
52
56
|
/** A folder created from a message's own Move to prompt, for the folder sidebar to pick up. */
|
|
53
57
|
onFolderCreated?: (folder: Folder) => void;
|
|
58
|
+
/** Registers the keyboard shortcuts (Reply, Reply all, Forward, Archive, Move to) of the message that was opened - see `MessageDetailPane`'s
|
|
59
|
+
* `shortcuts`. The caller turns it off while it is doing something else with the keyboard (select mode). */
|
|
60
|
+
shortcuts?: boolean;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
/**
|
|
@@ -110,8 +117,18 @@ async function loadThread(mailboxUid: string, conversationId: string): Promise<{
|
|
|
110
117
|
return { messages, truncated: more };
|
|
111
118
|
}
|
|
112
119
|
|
|
120
|
+
/** Whether the browser is showing a focus ring on `element` - it was focused from the keyboard (or by a key press's handler), not by a click. */
|
|
121
|
+
function hasFocusRing(element: HTMLElement): boolean {
|
|
122
|
+
try {
|
|
123
|
+
return element.matches(":focus-visible");
|
|
124
|
+
} catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
113
129
|
/**
|
|
114
|
-
* The reading pane for the conversation list: the
|
|
130
|
+
* The reading pane for the conversation list: the conversation's subject in a card of its own, pinned at the top, and under it the whole thread as
|
|
131
|
+
* a stack of message cards (each exactly as tall as its message; the list scrolls as a whole), **newest at the top**, opened at the message
|
|
115
132
|
* the reader picked. Every message from that one through to the newest is expanded - which in this order is
|
|
116
133
|
* the opened message and the entries above it - and the older ones, below it, are collapsed to a one-line
|
|
117
134
|
* summary (sender, date, preview) that expands on click or Enter. So opening the newest message shows just
|
|
@@ -137,7 +154,9 @@ export default function ConversationThreadPane({
|
|
|
137
154
|
onMessageRemoved,
|
|
138
155
|
onLabelCreated,
|
|
139
156
|
onFolderCreated,
|
|
157
|
+
shortcuts,
|
|
140
158
|
}: ConversationThreadPaneProps) {
|
|
159
|
+
const { trackMessageChange } = useMailShell();
|
|
141
160
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
142
161
|
const [attachmentsByUid, setAttachmentsByUid] = useState<Record<string, Attachment[]>>({});
|
|
143
162
|
const [expandedUids, setExpandedUids] = useState<Set<string>>(new Set());
|
|
@@ -164,6 +183,9 @@ export default function ConversationThreadPane({
|
|
|
164
183
|
/** Where the toggled message's header sat in the viewport before it expanded, so the run below can put
|
|
165
184
|
* it back there - expanding a message above the one being read must not shove that one off-screen. */
|
|
166
185
|
const anchorRef = useRef<{ uid: string; top: number } | null>(null);
|
|
186
|
+
/** The message whose header button had the focus when it was toggled: expanding or collapsing swaps that button for the other state's, so the
|
|
187
|
+
* focus is put back on the new one. */
|
|
188
|
+
const refocusRef = useRef<string | null>(null);
|
|
167
189
|
|
|
168
190
|
const conversationId = conversation?.conversationId;
|
|
169
191
|
|
|
@@ -238,8 +260,15 @@ export default function ConversationThreadPane({
|
|
|
238
260
|
scroller.scrollTop += top;
|
|
239
261
|
}
|
|
240
262
|
}
|
|
241
|
-
//
|
|
242
|
-
|
|
263
|
+
// A key press (j, k, the arrows) that opened this thread left the focus on its list row on purpose, so Enter goes on to open the
|
|
264
|
+
// message's page; taking the focus here would make Enter toggle this header instead. A click leaves no focus ring, and hands
|
|
265
|
+
// the focus to the thread as before.
|
|
266
|
+
const active = document.activeElement;
|
|
267
|
+
if (!(active instanceof HTMLElement && active.hasAttribute("data-row-open") && hasFocusRing(active))) {
|
|
268
|
+
// `preventScroll` so focusing doesn't scroll it somewhere else again.
|
|
269
|
+
// (A card's header button is drawn by the message pane, which registers it; a stand-in pane that doesn't has none to focus.)
|
|
270
|
+
headerRefs.current[pendingFocusUid]?.focus({ preventScroll: true });
|
|
271
|
+
}
|
|
243
272
|
setPendingFocusUid(null);
|
|
244
273
|
}, [pendingFocusUid, expandedUids]);
|
|
245
274
|
|
|
@@ -252,6 +281,11 @@ export default function ConversationThreadPane({
|
|
|
252
281
|
// The row is still there: the anchor was taken from a rendered row, and toggling never removes one.
|
|
253
282
|
const row = rowRefs.current[anchor.uid]!;
|
|
254
283
|
scrollingAncestor(row).scrollTop += row.getBoundingClientRect().top - anchor.top;
|
|
284
|
+
// The button the reader had focused is gone (a collapsed card and an expanded one each draw their own): keep them where they were.
|
|
285
|
+
if (refocusRef.current === anchor.uid) {
|
|
286
|
+
headerRefs.current[anchor.uid]?.focus({ preventScroll: true });
|
|
287
|
+
}
|
|
288
|
+
refocusRef.current = null;
|
|
255
289
|
}, [expandedUids]);
|
|
256
290
|
|
|
257
291
|
// Attachments and mark-as-read, for expanded messages only - mirrors `mailDetailHooks.ts`'s
|
|
@@ -276,20 +310,26 @@ export default function ConversationThreadPane({
|
|
|
276
310
|
attachmentsRequestedRef.current.delete(uid);
|
|
277
311
|
});
|
|
278
312
|
}
|
|
279
|
-
if (!
|
|
313
|
+
if (!markReadRequestedRef.current.has(uid)) {
|
|
314
|
+
// Asked once per opened conversation and per expanding of the message: a failure is not retried from here, because
|
|
315
|
+
// undoing the optimistic change changes `messages`, which would run this effect again and ask again, for ever. A message
|
|
316
|
+
// that is already read is asked about too (there is nothing to send), so marking it unread while it is open - the
|
|
317
|
+
// keyboard's Ctrl+U - doesn't make this run again and read it straight back.
|
|
280
318
|
markReadRequestedRef.current.add(uid);
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
319
|
+
if (message.flags.read === true) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
// `message` is this render's copy, so the request carries its current `version`. The row, the conversation row's
|
|
323
|
+
// unread count and the folder badge all change at once; `previous` is what tells the list's conversation row
|
|
324
|
+
// that its count moved (see `setReadState()`).
|
|
325
|
+
void setReadState(message, true, {
|
|
326
|
+
patch: (updated, previous) => {
|
|
327
|
+
if (generation === generationRef.current) {
|
|
328
|
+
patchMessage(updated, previous);
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
track: trackMessageChange,
|
|
332
|
+
});
|
|
293
333
|
}
|
|
294
334
|
}
|
|
295
335
|
}, [expandedUids, messages]);
|
|
@@ -308,8 +348,11 @@ export default function ConversationThreadPane({
|
|
|
308
348
|
}
|
|
309
349
|
|
|
310
350
|
function toggleExpanded(uid: string) {
|
|
351
|
+
// Opening a message again asks to mark it read again - that is how one whose request failed is retried.
|
|
352
|
+
markReadRequestedRef.current.delete(uid);
|
|
311
353
|
// The row this button lives in has rendered, so its ref is set.
|
|
312
354
|
anchorRef.current = { uid, top: rowRefs.current[uid]!.getBoundingClientRect().top };
|
|
355
|
+
refocusRef.current = document.activeElement === headerRefs.current[uid] ? uid : null;
|
|
313
356
|
setExpandedUids((prev) => {
|
|
314
357
|
const next = new Set(prev);
|
|
315
358
|
if (next.has(uid)) {
|
|
@@ -321,6 +364,10 @@ export default function ConversationThreadPane({
|
|
|
321
364
|
});
|
|
322
365
|
}
|
|
323
366
|
|
|
367
|
+
// The keyboard acts on the message that was opened - or the newest, when the opened one isn't in this thread - never on all the expanded
|
|
368
|
+
// ones at once (each is a `MessageDetailPane`, and two of them must not both answer Ctrl+R).
|
|
369
|
+
const keyboardUid = messages.some((message) => message.uid === selectedUid) ? selectedUid : messages[0]?.uid;
|
|
370
|
+
|
|
324
371
|
function folderTypeOf(message: Message): string | undefined {
|
|
325
372
|
return folders.find((folder) => folder.uid === message.folderUid)?.type;
|
|
326
373
|
}
|
|
@@ -328,107 +375,113 @@ export default function ConversationThreadPane({
|
|
|
328
375
|
if (!conversation) {
|
|
329
376
|
return <p className="p-8 text-sm text-text-muted">Select a conversation to read it.</p>;
|
|
330
377
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (error) {
|
|
335
|
-
return (
|
|
336
|
-
<div className="p-4 flex-1">
|
|
337
|
-
<Alert>{error}</Alert>
|
|
338
|
-
</div>
|
|
339
|
-
);
|
|
340
|
-
}
|
|
378
|
+
const subject = displaySubject(conversation.subject) || "(no subject)";
|
|
379
|
+
// The newest message that is open carries the Reply / Forward buttons at the foot of its card.
|
|
380
|
+
const footerUid = messages.find((message) => expandedUids.has(message.uid))?.uid;
|
|
341
381
|
|
|
342
382
|
return (
|
|
343
|
-
// The pane is the window's height, not the thread's: a full-height flex column whose
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
//
|
|
383
|
+
// The pane is the window's height, not the thread's: a full-height flex column whose header card is fixed and whose list of
|
|
384
|
+
// message cards is the one scrolling, growing child (`min-h-0`, or the list would stretch the column past the pane instead of
|
|
385
|
+
// scrolling inside it). Each card is exactly as tall as its message.
|
|
386
|
+
// The subject card is the same element while the messages load and once they are here (the subject and the count are known from the
|
|
387
|
+
// list's row), so nothing shifts or is drawn again when they arrive: a skeleton card per message (up to three) stands where they will be.
|
|
348
388
|
<div className="flex-1 min-w-0 min-h-0 flex flex-col">
|
|
349
|
-
<
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
389
|
+
<SubjectCard
|
|
390
|
+
subject={subject}
|
|
391
|
+
meta={
|
|
392
|
+
loading
|
|
393
|
+
? conversation.messageCount > 1
|
|
394
|
+
? `${conversation.messageCount} messages`
|
|
395
|
+
: undefined
|
|
396
|
+
: error
|
|
397
|
+
? undefined
|
|
398
|
+
: `${messages.length} message${messages.length === 1 ? "" : "s"}`
|
|
399
|
+
}
|
|
400
|
+
>
|
|
401
|
+
{truncated && !loading && !error && (
|
|
355
402
|
<p className="text-xs text-text-muted mt-1">
|
|
356
403
|
Only the oldest {THREAD_MESSAGE_LIMIT} messages of this conversation are shown here. The rest
|
|
357
404
|
are still in the message list.
|
|
358
405
|
</p>
|
|
359
406
|
)}
|
|
360
|
-
</
|
|
361
|
-
|
|
407
|
+
</SubjectCard>
|
|
408
|
+
{loading ? (
|
|
409
|
+
<SkeletonCards messageCount={conversation.messageCount} />
|
|
410
|
+
) : error ? (
|
|
411
|
+
<div className="p-2 sm:p-3">
|
|
412
|
+
<Alert>{error}</Alert>
|
|
413
|
+
</div>
|
|
414
|
+
) : (
|
|
415
|
+
<ul className="flex-1 min-h-0 overflow-y-auto p-2 sm:p-3 flex flex-col gap-3">
|
|
362
416
|
{messages.map((message) => {
|
|
363
417
|
const uid = message.uid;
|
|
364
418
|
const expanded = expandedUids.has(uid);
|
|
365
419
|
const bodyId = `thread-message-${uid}`;
|
|
420
|
+
const messageUnread = isUnread(message);
|
|
366
421
|
return (
|
|
367
422
|
<li
|
|
368
423
|
key={uid}
|
|
369
424
|
ref={(node) => {
|
|
370
425
|
rowRefs.current[uid] = node;
|
|
371
426
|
}}
|
|
372
|
-
|
|
373
|
-
// against the `<ul>`'s own resolved height - so its `MessageDetailPane` below
|
|
374
|
-
// has a full pane of height to fill and its body reaches the bottom of the
|
|
375
|
-
// window whatever that window's size is. `min-` rather than `h-`: a message
|
|
376
|
-
// whose header alone is taller than the pane still gets the room it needs.
|
|
377
|
-
className={["border-b border-border", expanded ? "flex flex-col min-h-full" : ""].join(" ")}
|
|
427
|
+
data-unread={messageUnread ? "true" : undefined}
|
|
378
428
|
>
|
|
379
|
-
|
|
380
|
-
<
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
429
|
+
{expanded ? (
|
|
430
|
+
<MessageDetailPane
|
|
431
|
+
inThread
|
|
432
|
+
threadSubject={subject}
|
|
433
|
+
threadHeader={{
|
|
434
|
+
bodyId,
|
|
435
|
+
unread: messageUnread,
|
|
436
|
+
onToggle: () => toggleExpanded(uid),
|
|
437
|
+
buttonRef: (node) => {
|
|
438
|
+
headerRefs.current[uid] = node;
|
|
439
|
+
},
|
|
384
440
|
}}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
{
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
onScheduledSendCanceled={removeMessage}
|
|
421
|
-
onArchived={removeMessage}
|
|
422
|
-
labels={labels}
|
|
423
|
-
onLabelsChanged={patchMessage}
|
|
424
|
-
onLabelCreated={onLabelCreated}
|
|
441
|
+
footer={uid === footerUid}
|
|
442
|
+
shortcuts={!!shortcuts && uid === keyboardUid}
|
|
443
|
+
message={message}
|
|
444
|
+
attachments={attachmentsByUid[uid] ?? []}
|
|
445
|
+
isSentItems={folderTypeOf(message) === "sent_items"}
|
|
446
|
+
isOutbox={folderTypeOf(message) === "outbox"}
|
|
447
|
+
draftsFolderUid={folders.find((folder) => folder.type === "drafts")?.uid}
|
|
448
|
+
folders={folders}
|
|
449
|
+
onMoved={removeMessage}
|
|
450
|
+
onFolderCreated={onFolderCreated}
|
|
451
|
+
onRecalled={patchMessage}
|
|
452
|
+
onReceiptHandled={patchMessage}
|
|
453
|
+
onScheduledSendCanceled={removeMessage}
|
|
454
|
+
onArchived={removeMessage}
|
|
455
|
+
labels={labels}
|
|
456
|
+
onLabelsChanged={patchMessage}
|
|
457
|
+
onLabelCreated={onLabelCreated}
|
|
458
|
+
/>
|
|
459
|
+
) : (
|
|
460
|
+
<>
|
|
461
|
+
<CollapsedCard
|
|
462
|
+
from={message.from}
|
|
463
|
+
date={new Date(message.receivedDate).toLocaleString()}
|
|
464
|
+
preview={message.bodyPreview || (message.encrypted ? <EncryptedPreview /> : "")}
|
|
465
|
+
unread={messageUnread}
|
|
466
|
+
senderClassName={senderClass(messageUnread)}
|
|
467
|
+
dateClassName={dateClass(messageUnread)}
|
|
468
|
+
buttonRef={(node) => {
|
|
469
|
+
headerRefs.current[uid] = node;
|
|
470
|
+
}}
|
|
471
|
+
buttonProps={{
|
|
472
|
+
onClick: () => toggleExpanded(uid),
|
|
473
|
+
"aria-expanded": false,
|
|
474
|
+
"aria-controls": bodyId,
|
|
475
|
+
}}
|
|
425
476
|
/>
|
|
426
|
-
|
|
427
|
-
|
|
477
|
+
<div id={bodyId} hidden />
|
|
478
|
+
</>
|
|
479
|
+
)}
|
|
428
480
|
</li>
|
|
429
481
|
);
|
|
430
482
|
})}
|
|
431
483
|
</ul>
|
|
484
|
+
)}
|
|
432
485
|
</div>
|
|
433
486
|
);
|
|
434
487
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
import React, { ComponentType, useEffect, useState } from "react";
|
|
6
|
+
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
7
|
+
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
8
|
+
import { ReadingPaneSkeleton } from "./reading/MessageCard.js";
|
|
9
|
+
import type MessageDetailPaneType from "./MessageDetailPane.js";
|
|
10
|
+
import type ConversationThreadPaneType from "./ConversationThreadPane.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The reading pane (`MessageDetailPane`, and the thread pane that stacks it) is only rendered once a message is selected, and
|
|
14
|
+
* it is the heaviest piece of the inbox after the compose window - message security, the body sanitizer, the labels and move
|
|
15
|
+
* menus - so it is a chunk of its own, downloaded when the browser is idle after the list has loaded (`prefetchReadingPane()`)
|
|
16
|
+
* and, at the latest, on the first selection. Nothing about the pane changes: these wrap the same components, with a skeleton
|
|
17
|
+
* while the chunk loads and a reload prompt if it can't.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately not `React.lazy()` + `<Suspense>`: a boundary that has shown its fallback holds the content back for up to 300 ms
|
|
20
|
+
* after the code arrives (React's fallback throttle), which would put a fifth of a second between a click and a pane whose
|
|
21
|
+
* code was already fetched. This reads the loaded component synchronously when there is one, so an already-loaded pane renders
|
|
22
|
+
* on the click's own frame, and it can retry a failed download.
|
|
23
|
+
*/
|
|
24
|
+
function lazyComponent<P extends object>(load: () => Promise<{ default: ComponentType<P> }>, skeleton: (props: P) => React.ReactNode) {
|
|
25
|
+
let loaded: ComponentType<P> | undefined;
|
|
26
|
+
let loading: Promise<ComponentType<P>> | undefined;
|
|
27
|
+
|
|
28
|
+
/** Starts (once) loading the component's code. Rejects if it can't be downloaded - and then a later call tries again. */
|
|
29
|
+
function start(): Promise<ComponentType<P>> {
|
|
30
|
+
loading ??= load().then(
|
|
31
|
+
(module) => (loaded = module.default),
|
|
32
|
+
(err) => {
|
|
33
|
+
loading = undefined;
|
|
34
|
+
throw err;
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
return loading;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function Lazy(props: P) {
|
|
41
|
+
const [, setLoadedNow] = useState(0);
|
|
42
|
+
const [failed, setFailed] = useState(false);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (loaded) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
let cancelled = false;
|
|
48
|
+
start().then(
|
|
49
|
+
() => !cancelled && setLoadedNow((n) => n + 1),
|
|
50
|
+
() => !cancelled && setFailed(true),
|
|
51
|
+
);
|
|
52
|
+
return () => {
|
|
53
|
+
cancelled = true;
|
|
54
|
+
};
|
|
55
|
+
}, []);
|
|
56
|
+
if (loaded) {
|
|
57
|
+
const Component = loaded;
|
|
58
|
+
return <Component {...props} />;
|
|
59
|
+
}
|
|
60
|
+
return failed ? <ChunkFailed /> : skeleton(props);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { Lazy, start };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A chunk that can't be downloaded (offline, or replaced by a deploy since this page loaded) is the one failure a reading
|
|
67
|
+
* pane can't recover from by itself: say so, with the fix, rather than leaving a blank pane. */
|
|
68
|
+
function ChunkFailed() {
|
|
69
|
+
return (
|
|
70
|
+
<div className="flex-1 p-6">
|
|
71
|
+
<Alert>
|
|
72
|
+
<p className="mb-2">This part of the page couldn’t be loaded. Check your connection, then reload.</p>
|
|
73
|
+
<Button type="button" variant="secondary" className="!w-auto" onClick={() => window.location.reload()}>
|
|
74
|
+
Reload
|
|
75
|
+
</Button>
|
|
76
|
+
</Alert>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// While the code loads the pane already draws its frame - the header card with the subject the list row knows, and a card of placeholder lines - so
|
|
82
|
+
// the click is answered on its own frame and the messages only fill in.
|
|
83
|
+
const messageDetailPane = lazyComponent<React.ComponentProps<typeof MessageDetailPaneType>>(
|
|
84
|
+
() => import("./MessageDetailPane.js"),
|
|
85
|
+
// `LazyMessageDetailPane` renders the empty state, not this, for no message.
|
|
86
|
+
(props) => <ReadingPaneSkeleton subject={props.message!.subject} />,
|
|
87
|
+
);
|
|
88
|
+
const conversationThreadPane = lazyComponent<React.ComponentProps<typeof ConversationThreadPaneType>>(
|
|
89
|
+
() => import("./ConversationThreadPane.js"),
|
|
90
|
+
(props) => <ReadingPaneSkeleton subject={props.conversation!.subject} messageCount={props.conversation!.messageCount} />,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
/** Starts downloading the reading pane's code, for the page to call when it has nothing better to do. Never rejects. */
|
|
94
|
+
export function prefetchReadingPane(): void {
|
|
95
|
+
messageDetailPane.start().catch(() => undefined);
|
|
96
|
+
conversationThreadPane.start().catch(() => undefined);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** With nothing selected the pane is only its empty-state line, which needs none of the pane's code - so the chunk isn't
|
|
100
|
+
* requested until something is (or the page prefetches it). */
|
|
101
|
+
export function LazyMessageDetailPane(props: React.ComponentProps<typeof MessageDetailPaneType>) {
|
|
102
|
+
if (!props.message) {
|
|
103
|
+
return <p className="p-8 text-sm text-text-muted">Select a message to read it.</p>;
|
|
104
|
+
}
|
|
105
|
+
return <messageDetailPane.Lazy {...props} />;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function LazyConversationThreadPane(props: React.ComponentProps<typeof ConversationThreadPaneType>) {
|
|
109
|
+
if (!props.conversation) {
|
|
110
|
+
return <p className="p-8 text-sm text-text-muted">Select a conversation to read it.</p>;
|
|
111
|
+
}
|
|
112
|
+
return <conversationThreadPane.Lazy {...props} />;
|
|
113
|
+
}
|