@rapidmx/web-client 0.30.0 → 0.32.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/apps/admin/audit-log/index.tsx +3 -3
- package/apps/admin/branding/index.tsx +3 -3
- package/apps/admin/data-requests/index.tsx +18 -7
- package/apps/admin/distribution-lists/[uid].tsx +3 -3
- package/apps/admin/distribution-lists/index.tsx +3 -3
- package/apps/admin/distribution-lists/new/index.tsx +3 -3
- package/apps/admin/domains/[uid].tsx +6 -5
- package/apps/admin/domains/index.tsx +3 -3
- package/apps/admin/domains/new/index.tsx +3 -3
- package/apps/admin/escrow-scopes/[uid].tsx +11 -5
- package/apps/admin/escrow-scopes/index.tsx +3 -3
- package/apps/admin/escrow-scopes/new/index.tsx +9 -3
- package/apps/admin/index.tsx +4 -4
- package/apps/admin/ingest-queue/index.tsx +3 -3
- package/apps/admin/mailboxes/[uid].tsx +15 -7
- package/apps/admin/quarantine/index.tsx +4 -4
- package/apps/admin/retention-policy/index.tsx +3 -3
- package/apps/admin/signing-certificates/index.tsx +3 -3
- package/apps/admin/transport-rules/[uid].tsx +4 -4
- package/apps/admin/transport-rules/index.tsx +3 -3
- package/apps/admin/transport-rules/new/index.tsx +3 -3
- package/apps/escrow/matters/[uid].tsx +26 -2
- package/apps/escrow/matters/new/index.tsx +4 -0
- package/apps/shared/calendar/calendarLiveUpdates.ts +11 -10
- package/apps/shared/calendar/useCalendarReminders.ts +2 -0
- package/apps/shared/components/admin/ActionAlert.tsx +32 -0
- package/apps/shared/components/admin/diagnostics/InformationPanel.tsx +3 -3
- package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +2 -2
- package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
- package/apps/shared/components/admin/elevation.ts +19 -0
- package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +58 -3
- package/apps/shared/components/admin/layout/AdminShell.tsx +6 -1
- package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +3 -3
- package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +4 -4
- package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +4 -4
- package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +3 -3
- package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +6 -6
- package/apps/shared/components/admin/settings/BrandingForm.tsx +22 -9
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +4 -4
- package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +33 -4
- package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +3 -3
- package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +8 -3
- package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +3 -3
- package/apps/shared/components/admin/settings/PluginsManager.tsx +195 -46
- package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +25 -12
- package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +40 -7
- package/apps/shared/components/admin/setup/SetupWizard.tsx +8 -6
- package/apps/shared/components/admin/signOut.ts +3 -0
- package/apps/shared/components/admin/usePagedList.tsx +2 -2
- package/apps/shared/components/calendar/DescriptionEditor.tsx +2 -1
- package/apps/shared/components/calendar/EventDetails.tsx +34 -8
- package/apps/shared/components/calendar/EventEditor.tsx +90 -40
- package/apps/shared/components/calendar/EventFormParts.tsx +5 -2
- package/apps/shared/components/calendar/EventMatchNav.tsx +14 -2
- package/apps/shared/components/calendar/EventModal.tsx +25 -1
- package/apps/shared/components/calendar/FindATime.tsx +10 -2
- package/apps/shared/components/calendar/ListView.tsx +99 -25
- package/apps/shared/components/calendar/RecurrenceEditor.tsx +19 -8
- package/apps/shared/components/calendar/SplitDayView.tsx +14 -18
- package/apps/shared/components/calendar/TaskQuickForm.tsx +3 -2
- package/apps/shared/components/calendar/TimeGridView.tsx +35 -19
- package/apps/shared/components/calendar/gridLayout.ts +94 -0
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +1 -1
- package/apps/shared/components/calendar/timePicker.ts +5 -2
- package/apps/shared/components/contacts/ContactCard.tsx +15 -4
- package/apps/shared/components/contacts/ContactDetailPane.tsx +54 -15
- package/apps/shared/components/contacts/ContactForm.tsx +61 -17
- package/apps/shared/components/contacts/ContactPhotoEditor.tsx +88 -0
- package/apps/shared/components/contacts/ContactPhotoField.tsx +36 -39
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +1 -1
- package/apps/shared/components/contacts/participantDetails.ts +18 -1
- package/apps/shared/components/layout/AppShell.tsx +9 -0
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +9 -4
- package/apps/shared/components/layout/MailboxProvisioning.tsx +31 -3
- package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +4 -2
- package/apps/shared/components/mail/ConversationList.tsx +49 -6
- package/apps/shared/components/mail/ConversationThreadPane.tsx +1 -1
- package/apps/shared/components/mail/MenuButton.tsx +3 -400
- package/apps/shared/components/mail/MessageDetailPane.tsx +16 -13
- package/apps/shared/components/mail/compose/ComposeToolbar.tsx +11 -1
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +57 -11
- package/apps/shared/components/mail/compose/GifPicker.tsx +10 -4
- package/apps/shared/components/mail/compose/RecipientInput.tsx +9 -7
- package/apps/shared/components/mail/compose/composeFlushRegistry.ts +14 -1
- package/apps/shared/components/mail/compose/encryptionRequirement.ts +5 -0
- package/apps/shared/components/mail/compose/quotedBody.ts +31 -6
- package/apps/shared/components/mail/layout/MailShell.tsx +1 -1
- package/apps/shared/components/mail/reading/MessageBody.tsx +13 -7
- package/apps/shared/components/mail/reading/bodyContent.ts +2 -0
- package/apps/shared/components/mail/reading/bodyHtml.ts +25 -2
- package/apps/shared/components/mail/writableMailboxes.ts +2 -0
- package/apps/shared/components/rules/RuleBuilder.tsx +11 -2
- package/apps/shared/components/settings/GravatarSetting.tsx +27 -0
- package/apps/shared/components/settings/layout/SettingsShell.tsx +1 -1
- package/apps/shared/components/tasks/dueDate.ts +29 -0
- package/apps/shared/components/tasks/layout/TasksShell.tsx +1 -1
- package/apps/shared/mail/decryptedMessages.ts +51 -2
- package/apps/shared/mail/listSnapshots.ts +2 -0
- package/apps/shared/mail/outbox/sendDecision.ts +14 -3
- package/apps/shared/mail/outbox/sendJob.ts +5 -0
- package/apps/shared/mail/useMailLiveUpdates.ts +7 -1
- package/apps/shared/mail/usePermanentDelete.tsx +3 -1
- package/apps/shared/notifications/apiErrors.ts +2 -0
- package/apps/shared/notifications/preferences.ts +1 -1
- package/apps/shared/notifications/store.ts +3 -1
- package/apps/shared/plugins/pluginNav.ts +4 -2
- package/apps/www/calendar/index.tsx +24 -12
- package/apps/www/contacts/[uid].tsx +6 -1
- package/apps/www/contacts/index.tsx +66 -12
- package/apps/www/index.tsx +91 -28
- package/apps/www/messages/[uid].tsx +8 -3
- package/apps/www/settings/auto-reply/index.tsx +13 -0
- package/apps/www/settings/blocked-senders/index.tsx +4 -0
- package/apps/www/settings/encryption/index.tsx +66 -4
- package/apps/www/settings/privacy/index.tsx +4 -1
- package/apps/www/settings/profile/index.tsx +12 -1
- package/apps/www/settings/sharing/index.tsx +11 -1
- package/apps/www/settings/signatures/[uid].tsx +6 -4
- package/apps/www/tasks/index.tsx +97 -29
- package/dist/apps/admin/audit-log/index.js +3 -3
- package/dist/apps/admin/branding/index.js +3 -3
- package/dist/apps/admin/data-requests/index.js +17 -7
- package/dist/apps/admin/distribution-lists/[uid].js +3 -3
- package/dist/apps/admin/distribution-lists/index.js +3 -3
- package/dist/apps/admin/distribution-lists/new/index.js +3 -3
- package/dist/apps/admin/domains/[uid].js +6 -5
- package/dist/apps/admin/domains/index.js +3 -3
- package/dist/apps/admin/domains/new/index.js +3 -3
- package/dist/apps/admin/escrow-scopes/[uid].js +11 -6
- package/dist/apps/admin/escrow-scopes/index.js +3 -3
- package/dist/apps/admin/escrow-scopes/new/index.js +9 -4
- package/dist/apps/admin/index.js +4 -4
- package/dist/apps/admin/ingest-queue/index.js +3 -3
- package/dist/apps/admin/mailboxes/[uid].js +15 -7
- package/dist/apps/admin/quarantine/index.js +4 -4
- package/dist/apps/admin/retention-policy/index.js +3 -3
- package/dist/apps/admin/signing-certificates/index.js +3 -3
- package/dist/apps/admin/transport-rules/[uid].js +4 -4
- package/dist/apps/admin/transport-rules/index.js +3 -3
- package/dist/apps/admin/transport-rules/new/index.js +3 -3
- package/dist/apps/escrow/matters/[uid].js +8 -3
- package/dist/apps/escrow/matters/new/index.js +4 -0
- package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +11 -7
- package/dist/apps/shared/calendar/calendarLiveUpdates.js +10 -9
- package/dist/apps/shared/calendar/useCalendarReminders.js +2 -0
- package/dist/apps/shared/components/admin/ActionAlert.d.ts +13 -0
- package/dist/apps/shared/components/admin/ActionAlert.js +24 -0
- package/dist/apps/shared/components/admin/diagnostics/InformationPanel.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.d.ts +2 -2
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
- package/dist/apps/shared/components/admin/elevation.d.ts +9 -0
- package/dist/apps/shared/components/admin/elevation.js +16 -0
- package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.d.ts +2 -0
- package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +41 -1
- package/dist/apps/shared/components/admin/layout/AdminShell.js +2 -1
- package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +3 -3
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +4 -4
- package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +3 -3
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +6 -6
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +17 -7
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +4 -4
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +17 -5
- package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +3 -3
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +8 -3
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +3 -3
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +103 -18
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +17 -10
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +18 -8
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +8 -6
- package/dist/apps/shared/components/admin/signOut.js +3 -0
- package/dist/apps/shared/components/admin/usePagedList.js +2 -2
- package/dist/apps/shared/components/calendar/DescriptionEditor.js +2 -1
- package/dist/apps/shared/components/calendar/EventDetails.d.ts +3 -1
- package/dist/apps/shared/components/calendar/EventDetails.js +7 -4
- package/dist/apps/shared/components/calendar/EventEditor.js +81 -33
- package/dist/apps/shared/components/calendar/EventFormParts.d.ts +3 -1
- package/dist/apps/shared/components/calendar/EventFormParts.js +3 -3
- package/dist/apps/shared/components/calendar/EventMatchNav.d.ts +1 -1
- package/dist/apps/shared/components/calendar/EventMatchNav.js +12 -2
- package/dist/apps/shared/components/calendar/EventModal.js +10 -2
- package/dist/apps/shared/components/calendar/FindATime.js +4 -3
- package/dist/apps/shared/components/calendar/ListView.d.ts +10 -4
- package/dist/apps/shared/components/calendar/ListView.js +92 -27
- package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +4 -1
- package/dist/apps/shared/components/calendar/RecurrenceEditor.js +15 -10
- package/dist/apps/shared/components/calendar/SplitDayView.js +11 -17
- package/dist/apps/shared/components/calendar/TaskQuickForm.js +3 -2
- package/dist/apps/shared/components/calendar/TimeGridView.js +23 -21
- package/dist/apps/shared/components/calendar/gridLayout.d.ts +41 -0
- package/dist/apps/shared/components/calendar/gridLayout.js +83 -0
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +1 -1
- package/dist/apps/shared/components/calendar/timePicker.js +5 -2
- package/dist/apps/shared/components/contacts/ContactCard.js +9 -3
- package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +5 -3
- package/dist/apps/shared/components/contacts/ContactDetailPane.js +11 -5
- package/dist/apps/shared/components/contacts/ContactForm.js +54 -16
- package/dist/apps/shared/components/contacts/ContactPhotoEditor.d.ts +21 -0
- package/dist/apps/shared/components/contacts/ContactPhotoEditor.js +56 -0
- package/dist/apps/shared/components/contacts/ContactPhotoField.d.ts +7 -4
- package/dist/apps/shared/components/contacts/ContactPhotoField.js +19 -21
- package/dist/apps/shared/components/contacts/layout/ContactsShell.js +1 -1
- package/dist/apps/shared/components/contacts/participantDetails.d.ts +4 -1
- package/dist/apps/shared/components/contacts/participantDetails.js +17 -1
- package/dist/apps/shared/components/layout/AppShell.js +9 -0
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +2 -3
- package/dist/apps/shared/components/layout/MailboxProvisioning.d.ts +4 -1
- package/dist/apps/shared/components/layout/MailboxProvisioning.js +24 -2
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +4 -2
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +4 -2
- package/dist/apps/shared/components/mail/ConversationList.js +49 -6
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +1 -1
- package/dist/apps/shared/components/mail/MenuButton.d.ts +2 -78
- package/dist/apps/shared/components/mail/MenuButton.js +2 -211
- package/dist/apps/shared/components/mail/MessageDetailPane.js +10 -6
- package/dist/apps/shared/components/mail/compose/ComposeToolbar.d.ts +3 -0
- package/dist/apps/shared/components/mail/compose/ComposeToolbar.js +10 -1
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +55 -10
- package/dist/apps/shared/components/mail/compose/GifPicker.js +10 -4
- package/dist/apps/shared/components/mail/compose/RecipientInput.js +9 -7
- package/dist/apps/shared/components/mail/compose/composeFlushRegistry.d.ts +7 -1
- package/dist/apps/shared/components/mail/compose/composeFlushRegistry.js +13 -1
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +5 -0
- package/dist/apps/shared/components/mail/compose/quotedBody.js +28 -5
- package/dist/apps/shared/components/mail/layout/MailShell.js +1 -1
- package/dist/apps/shared/components/mail/reading/MessageBody.js +11 -6
- package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -0
- package/dist/apps/shared/components/mail/reading/bodyHtml.d.ts +5 -1
- package/dist/apps/shared/components/mail/reading/bodyHtml.js +23 -2
- package/dist/apps/shared/components/mail/writableMailboxes.js +2 -0
- package/dist/apps/shared/components/rules/RuleBuilder.js +9 -1
- package/dist/apps/shared/components/settings/GravatarSetting.d.ts +6 -0
- package/dist/apps/shared/components/settings/GravatarSetting.js +15 -0
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +1 -1
- package/dist/apps/shared/components/tasks/dueDate.d.ts +12 -0
- package/dist/apps/shared/components/tasks/dueDate.js +26 -0
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +1 -1
- package/dist/apps/shared/mail/decryptedMessages.d.ts +8 -0
- package/dist/apps/shared/mail/decryptedMessages.js +46 -2
- package/dist/apps/shared/mail/listSnapshots.js +2 -0
- package/dist/apps/shared/mail/outbox/sendDecision.d.ts +6 -3
- package/dist/apps/shared/mail/outbox/sendDecision.js +13 -3
- package/dist/apps/shared/mail/outbox/sendJob.d.ts +1 -0
- package/dist/apps/shared/mail/outbox/sendJob.js +5 -1
- package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +3 -0
- package/dist/apps/shared/mail/useMailLiveUpdates.js +6 -1
- package/dist/apps/shared/mail/usePermanentDelete.d.ts +2 -0
- package/dist/apps/shared/mail/usePermanentDelete.js +1 -1
- package/dist/apps/shared/notifications/apiErrors.js +2 -0
- package/dist/apps/shared/notifications/preferences.d.ts +1 -1
- package/dist/apps/shared/notifications/preferences.js +1 -1
- package/dist/apps/shared/notifications/store.js +3 -1
- package/dist/apps/shared/plugins/pluginNav.d.ts +2 -1
- package/dist/apps/shared/plugins/pluginNav.js +4 -2
- package/dist/apps/www/calendar/index.js +24 -12
- package/dist/apps/www/contacts/[uid].js +6 -2
- package/dist/apps/www/contacts/index.js +62 -11
- package/dist/apps/www/index.js +85 -30
- package/dist/apps/www/messages/[uid].js +8 -3
- package/dist/apps/www/settings/auto-reply/index.js +13 -1
- package/dist/apps/www/settings/blocked-senders/index.js +4 -0
- package/dist/apps/www/settings/encryption/index.js +29 -5
- package/dist/apps/www/settings/privacy/index.js +3 -2
- package/dist/apps/www/settings/profile/index.js +4 -1
- package/dist/apps/www/settings/sharing/index.js +10 -1
- package/dist/apps/www/settings/signatures/[uid].js +6 -4
- package/dist/apps/www/tasks/index.js +94 -32
- package/dist/lib/admin/auditLogApi.js +2 -2
- package/dist/lib/admin/distributionListsApi.js +2 -2
- package/dist/lib/admin/domainsApi.js +2 -2
- package/dist/lib/admin/escrowAuditLogApi.js +2 -2
- package/dist/lib/admin/escrowScopesApi.js +2 -2
- package/dist/lib/admin/pluginsApi.d.ts +31 -2
- package/dist/lib/admin/pluginsApi.js +23 -1
- package/dist/lib/admin/retentionPolicyApi.d.ts +3 -0
- package/dist/lib/admin/retentionPolicyApi.js +3 -0
- package/dist/lib/admin/transportRulesApi.js +2 -2
- package/dist/lib/appearance/preferencesApi.d.ts +1 -1
- package/dist/lib/appearance/preferencesApi.js +3 -14
- package/dist/lib/auth/session.d.ts +51 -12
- package/dist/lib/auth/session.js +214 -38
- package/dist/lib/branding/brandingApi.d.ts +3 -3
- package/dist/lib/branding/brandingApi.js +9 -21
- package/dist/lib/calendar/calendarApi.js +3 -1
- package/dist/lib/calendar/eventSearch.js +43 -6
- package/dist/lib/calendar/freeBusyApi.d.ts +2 -1
- package/dist/lib/calendar/freeBusyApi.js +13 -5
- package/dist/lib/calendar/recurrence.js +51 -3
- package/dist/lib/calendar/useDayKey.d.ts +7 -0
- package/dist/lib/calendar/useDayKey.js +43 -0
- package/dist/lib/components/avatar/ContactAvatar.d.ts +6 -2
- package/dist/lib/components/avatar/ContactAvatar.js +37 -8
- package/dist/lib/components/avatar/ContactPhotoAvatar.d.ts +11 -0
- package/dist/lib/components/avatar/ContactPhotoAvatar.js +18 -0
- package/dist/lib/components/forms/CameraCaptureModal.d.ts +17 -0
- package/dist/lib/components/forms/CameraCaptureModal.js +88 -0
- package/dist/lib/components/forms/ImageEditBadge.d.ts +28 -0
- package/dist/lib/components/forms/ImageEditBadge.js +62 -0
- package/dist/lib/components/menus/MenuButton.d.ts +78 -0
- package/dist/lib/components/menus/MenuButton.js +219 -0
- package/dist/lib/components/navigation/BottomTabBar.d.ts +3 -1
- package/dist/lib/components/navigation/BottomTabBar.js +22 -3
- package/dist/lib/components/overlays/PopoverPortal.d.ts +2 -1
- package/dist/lib/components/overlays/PopoverPortal.js +3 -2
- package/dist/lib/components/pickers/TimeZonePicker.js +13 -1
- package/dist/lib/contacts/contactsApi.d.ts +5 -2
- package/dist/lib/contacts/contactsApi.js +16 -19
- package/dist/lib/contacts/gravatar.d.ts +14 -3
- package/dist/lib/contacts/gravatar.js +42 -5
- package/dist/lib/contacts/preparePhoto.d.ts +24 -0
- package/dist/lib/contacts/preparePhoto.js +250 -0
- package/dist/lib/contacts/useContactPhotoSrc.d.ts +19 -0
- package/dist/lib/contacts/useContactPhotoSrc.js +153 -0
- package/dist/lib/contacts/vcard.d.ts +3 -0
- package/dist/lib/contacts/vcard.js +59 -7
- package/dist/lib/crypto/escrowKeys.d.ts +8 -0
- package/dist/lib/crypto/escrowKeys.js +20 -1
- package/dist/lib/crypto/messageSecurity.d.ts +7 -4
- package/dist/lib/crypto/messageSecurity.js +23 -5
- package/dist/lib/crypto/passwordUnlock.d.ts +6 -1
- package/dist/lib/crypto/passwordUnlock.js +13 -2
- package/dist/lib/crypto/smime.js +12 -2
- package/dist/lib/mail/compose/composeQuoting.js +3 -2
- package/dist/lib/mail/compose/quotedImages.d.ts +6 -3
- package/dist/lib/mail/compose/quotedImages.js +35 -9
- package/dist/lib/mail/labelsApi.js +2 -2
- package/dist/lib/mail/mailApi.d.ts +8 -5
- package/dist/lib/mail/mailApi.js +73 -39
- package/dist/lib/mail/mailDetailHooks.js +11 -3
- package/dist/lib/mail/mailFilterRulesApi.js +2 -2
- package/dist/lib/mail/mailSignaturesApi.js +2 -2
- package/dist/lib/mail/mailboxImportApi.d.ts +1 -1
- package/dist/lib/mail/mailboxImportApi.js +3 -15
- package/dist/lib/mail/messageBodySanitizer.d.ts +6 -0
- package/dist/lib/mail/messageBodySanitizer.js +44 -1
- package/dist/lib/search/queryGrammar.js +12 -4
- package/dist/lib/search/searchTier3.d.ts +6 -1
- package/dist/lib/search/searchTier3.js +54 -25
- package/dist/lib/tasks/tasksApi.js +3 -3
- package/dist/lib/util/api.d.ts +26 -4
- package/dist/lib/util/api.js +73 -16
- package/dist/lib/util/apiQuery.d.ts +11 -0
- package/dist/lib/util/apiQuery.js +11 -0
- package/dist/lib/util/settleWithLimit.d.ts +5 -0
- package/dist/lib/util/settleWithLimit.js +25 -0
- package/lib/admin/auditLogApi.ts +2 -2
- package/lib/admin/distributionListsApi.ts +2 -2
- package/lib/admin/domainsApi.ts +2 -2
- package/lib/admin/escrowAuditLogApi.ts +2 -2
- package/lib/admin/escrowScopesApi.ts +2 -2
- package/lib/admin/pluginsApi.ts +45 -3
- package/lib/admin/retentionPolicyApi.ts +4 -0
- package/lib/admin/transportRulesApi.ts +2 -2
- package/lib/appearance/preferencesApi.ts +3 -14
- package/lib/auth/session.ts +239 -39
- package/lib/branding/brandingApi.ts +9 -21
- package/lib/calendar/calendarApi.ts +3 -1
- package/lib/calendar/eventSearch.ts +46 -6
- package/lib/calendar/freeBusyApi.ts +14 -6
- package/lib/calendar/recurrence.ts +59 -8
- package/lib/calendar/useDayKey.ts +45 -0
- package/lib/components/avatar/ContactAvatar.tsx +46 -8
- package/lib/components/avatar/ContactPhotoAvatar.tsx +26 -0
- package/lib/components/forms/CameraCaptureModal.tsx +133 -0
- package/lib/components/forms/ImageEditBadge.tsx +121 -0
- package/lib/components/menus/MenuButton.tsx +408 -0
- package/lib/components/navigation/BottomTabBar.tsx +30 -8
- package/lib/components/overlays/PopoverPortal.tsx +3 -2
- package/lib/components/pickers/TimeZonePicker.tsx +17 -1
- package/lib/contacts/contactsApi.ts +18 -19
- package/lib/contacts/gravatar.ts +47 -5
- package/lib/contacts/preparePhoto.ts +273 -0
- package/lib/contacts/useContactPhotoSrc.ts +177 -0
- package/lib/contacts/vcard.ts +60 -7
- package/lib/crypto/escrowKeys.ts +21 -1
- package/lib/crypto/messageSecurity.ts +33 -9
- package/lib/crypto/passwordUnlock.ts +14 -2
- package/lib/crypto/smime.ts +13 -2
- package/lib/mail/compose/composeQuoting.ts +3 -2
- package/lib/mail/compose/quotedImages.ts +39 -9
- package/lib/mail/labelsApi.ts +2 -2
- package/lib/mail/mailApi.ts +96 -39
- package/lib/mail/mailDetailHooks.ts +11 -3
- package/lib/mail/mailFilterRulesApi.ts +2 -2
- package/lib/mail/mailSignaturesApi.ts +2 -2
- package/lib/mail/mailboxImportApi.ts +9 -15
- package/lib/mail/messageBodySanitizer.ts +47 -1
- package/lib/search/queryGrammar.ts +13 -4
- package/lib/search/searchTier3.ts +65 -32
- package/lib/tasks/tasksApi.ts +3 -3
- package/lib/util/api.ts +99 -16
- package/lib/util/apiQuery.ts +13 -0
- package/lib/util/settleWithLimit.ts +25 -0
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { ChangeEvent, useEffect, useRef, useState } from "react";
|
|
6
|
-
import {
|
|
6
|
+
import { actionErrorMessage } from "../../shared/components/admin/elevation.js";
|
|
7
7
|
import {
|
|
8
8
|
AdminSigningEnrollment,
|
|
9
9
|
getSigningEnrollmentInfo,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
uploadSigningEnrollmentCertificate,
|
|
16
16
|
} from "../../../lib/crypto/signingProviderApi.js";
|
|
17
17
|
import AdminShell, { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
|
|
18
|
-
import Alert from "
|
|
18
|
+
import Alert from "../../shared/components/admin/ActionAlert.js";
|
|
19
19
|
import Button from "../../../lib/components/buttons/Button.js";
|
|
20
20
|
import Modal from "../../../lib/components/overlays/Modal.js";
|
|
21
21
|
import { notify } from "../../shared/notifications/store.js";
|
|
@@ -24,7 +24,7 @@ const INPUT_CLASS = "text-sm border border-border rounded-sm py-1.5 px-2 bg-surf
|
|
|
24
24
|
const TEXTAREA_CLASS = `${INPUT_CLASS} font-mono text-xs`;
|
|
25
25
|
|
|
26
26
|
function errorMessage(err: unknown, fallback: string): string {
|
|
27
|
-
return err
|
|
27
|
+
return actionErrorMessage(err, fallback);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function statusBadgeClass(status: AdminSigningEnrollment["status"]): string {
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { FormEvent, useEffect, useState } from "react";
|
|
6
|
-
import { ApiRequestError } from "../../../lib/util/api.js";
|
|
7
6
|
import {
|
|
8
7
|
getTransportRule,
|
|
9
8
|
TransportRule,
|
|
@@ -20,9 +19,10 @@ import {
|
|
|
20
19
|
TRANSPORT_RULE_ACTION_TYPES,
|
|
21
20
|
TRANSPORT_RULE_CONDITION_FIELDS,
|
|
22
21
|
} from "./_transportRuleConfig.js";
|
|
23
|
-
import Alert from "
|
|
22
|
+
import Alert from "../../shared/components/admin/ActionAlert.js";
|
|
24
23
|
import Button from "../../../lib/components/buttons/Button.js";
|
|
25
24
|
import FormField from "../../../lib/components/forms/FormField.js";
|
|
25
|
+
import { actionErrorMessage } from "../../shared/components/admin/elevation.js";
|
|
26
26
|
|
|
27
27
|
const INPUT_CLASS =
|
|
28
28
|
"w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -63,7 +63,7 @@ function TransportRuleDetailContent({ uid }: { uid: string }) {
|
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
})
|
|
66
|
-
.catch((err) => setError(err
|
|
66
|
+
.catch((err) => setError(actionErrorMessage(err, "Could not load this transport rule.")))
|
|
67
67
|
.finally(() => setLoading(false));
|
|
68
68
|
}, [uid]);
|
|
69
69
|
|
|
@@ -106,7 +106,7 @@ function TransportRuleDetailContent({ uid }: { uid: string }) {
|
|
|
106
106
|
setOriginal(updated);
|
|
107
107
|
setSaved(true);
|
|
108
108
|
} catch (err) {
|
|
109
|
-
setError(err
|
|
109
|
+
setError(actionErrorMessage(err, "Could not save this transport rule."));
|
|
110
110
|
} finally {
|
|
111
111
|
setSaving(false);
|
|
112
112
|
}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { useEffect, useState } from "react";
|
|
6
|
-
import { ApiRequestError } from "../../../lib/util/api.js";
|
|
7
6
|
import { listTransportRules, TransportRule } from "../../../lib/admin/transportRulesApi.js";
|
|
8
7
|
import AdminShell, { AdminShellProps } from "../../shared/components/admin/layout/AdminShell.js";
|
|
9
|
-
import Alert from "
|
|
8
|
+
import Alert from "../../shared/components/admin/ActionAlert.js";
|
|
10
9
|
import Button from "../../../lib/components/buttons/Button.js";
|
|
10
|
+
import { actionErrorMessage } from "../../shared/components/admin/elevation.js";
|
|
11
11
|
|
|
12
12
|
const PAGE_SIZE = 25;
|
|
13
13
|
|
|
@@ -48,7 +48,7 @@ function TransportRulesContent() {
|
|
|
48
48
|
useEffect(() => {
|
|
49
49
|
listAllTransportRules()
|
|
50
50
|
.then(setRules)
|
|
51
|
-
.catch((err) => setError(err
|
|
51
|
+
.catch((err) => setError(actionErrorMessage(err, "Could not load transport rules.")))
|
|
52
52
|
.finally(() => setLoading(false));
|
|
53
53
|
}, []);
|
|
54
54
|
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { FormEvent, useState } from "react";
|
|
6
6
|
import { useRouter } from "@rapidrest/react/client";
|
|
7
|
-
import { ApiRequestError } from "../../../../lib/util/api.js";
|
|
8
7
|
import {
|
|
9
8
|
createTransportRule,
|
|
10
9
|
TransportRuleAction,
|
|
@@ -19,9 +18,10 @@ import {
|
|
|
19
18
|
TRANSPORT_RULE_ACTION_TYPES,
|
|
20
19
|
TRANSPORT_RULE_CONDITION_FIELDS,
|
|
21
20
|
} from "../_transportRuleConfig.js";
|
|
22
|
-
import Alert from "
|
|
21
|
+
import Alert from "../../../shared/components/admin/ActionAlert.js";
|
|
23
22
|
import Button from "../../../../lib/components/buttons/Button.js";
|
|
24
23
|
import FormField from "../../../../lib/components/forms/FormField.js";
|
|
24
|
+
import { actionErrorMessage } from "../../../shared/components/admin/elevation.js";
|
|
25
25
|
|
|
26
26
|
const INPUT_CLASS =
|
|
27
27
|
"w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -77,7 +77,7 @@ function NewTransportRuleForm() {
|
|
|
77
77
|
const created = await createTransportRule({ name: name.trim(), ...rule });
|
|
78
78
|
void navigate(`/admin/transport-rules/${encodeURIComponent(created.uid)}`);
|
|
79
79
|
} catch (err) {
|
|
80
|
-
setError(err
|
|
80
|
+
setError(actionErrorMessage(err, "Could not create the transport rule."));
|
|
81
81
|
} finally {
|
|
82
82
|
setSaving(false);
|
|
83
83
|
}
|
|
@@ -77,6 +77,8 @@ function MatterDetailContent({ uid }: { uid: string }) {
|
|
|
77
77
|
const [closing, setClosing] = useState(false);
|
|
78
78
|
const [closeError, setCloseError] = useState<string | null>(null);
|
|
79
79
|
const [approveTarget, setApproveTarget] = useState<EscrowAccessRequest | null>(null);
|
|
80
|
+
// Denying cannot be undone - a denied request is never approved afterwards, only asked for again - so it is confirmed first.
|
|
81
|
+
const [denyTarget, setDenyTarget] = useState<EscrowAccessRequest | null>(null);
|
|
80
82
|
|
|
81
83
|
const [showNewRequest, setShowNewRequest] = useState(false);
|
|
82
84
|
const [newRequestMailboxUid, setNewRequestMailboxUid] = useState("");
|
|
@@ -184,7 +186,10 @@ function MatterDetailContent({ uid }: { uid: string }) {
|
|
|
184
186
|
}
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
|
|
189
|
+
// Only ever invoked from the deny-confirmation modal below, which only renders once `denyTarget` is set.
|
|
190
|
+
async function handleDeny() {
|
|
191
|
+
const request = denyTarget!;
|
|
192
|
+
setDenyTarget(null);
|
|
188
193
|
setActingOn((prev) => ({ ...prev, [request.uid]: true }));
|
|
189
194
|
setActionErrors((prev) => ({ ...prev, [request.uid]: "" }));
|
|
190
195
|
try {
|
|
@@ -381,7 +386,7 @@ function MatterDetailContent({ uid }: { uid: string }) {
|
|
|
381
386
|
className="!w-auto"
|
|
382
387
|
loading={actingOn[request.uid]}
|
|
383
388
|
disabled={actingOn[request.uid]}
|
|
384
|
-
onClick={() =>
|
|
389
|
+
onClick={() => setDenyTarget(request)}
|
|
385
390
|
>
|
|
386
391
|
Deny
|
|
387
392
|
</Button>
|
|
@@ -562,6 +567,25 @@ function MatterDetailContent({ uid }: { uid: string }) {
|
|
|
562
567
|
</div>
|
|
563
568
|
</Modal>
|
|
564
569
|
|
|
570
|
+
<Modal open={denyTarget !== null} onClose={() => setDenyTarget(null)} title="Deny access request">
|
|
571
|
+
<p className="text-sm mb-3">
|
|
572
|
+
Denying refuses this request for good: it can’t be approved afterwards, and holders can’t read the key material for
|
|
573
|
+
mailbox <span className="break-all font-semibold">{denyTarget?.mailboxUid}</span> through it. A new request has to be made to ask again.
|
|
574
|
+
</p>
|
|
575
|
+
<div className="flex gap-3 justify-end">
|
|
576
|
+
<Button type="button" variant="secondary" className="!w-auto" onClick={() => setDenyTarget(null)}>
|
|
577
|
+
Cancel
|
|
578
|
+
</Button>
|
|
579
|
+
<Button
|
|
580
|
+
type="button"
|
|
581
|
+
className="!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger"
|
|
582
|
+
onClick={() => void handleDeny()}
|
|
583
|
+
>
|
|
584
|
+
Deny request
|
|
585
|
+
</Button>
|
|
586
|
+
</div>
|
|
587
|
+
</Modal>
|
|
588
|
+
|
|
565
589
|
<Modal open={showNewRequest} onClose={() => setShowNewRequest(false)} title="New access request">
|
|
566
590
|
<form onSubmit={handleCreateRequest} className="flex flex-col gap-4">
|
|
567
591
|
{newRequestError && <Alert>{newRequestError}</Alert>}
|
|
@@ -59,6 +59,10 @@ function NewMatterForm() {
|
|
|
59
59
|
setError("At least one custodian mailbox is required.");
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
|
+
if (Number.isNaN(new Date(dateRangeStart).getTime()) || Number.isNaN(new Date(dateRangeEnd).getTime())) {
|
|
63
|
+
setError("The date range start and end are both required.");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
62
66
|
if (new Date(dateRangeStart).getTime() >= new Date(dateRangeEnd).getTime()) {
|
|
63
67
|
setError("The date range start must be before its end.");
|
|
64
68
|
return;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
-
/** Pure helper behind the calendar page's live updates: which push events say "an event changed
|
|
5
|
+
/** Pure helper behind the calendar page's live updates: which push events say "an event changed". */
|
|
6
6
|
|
|
7
7
|
import type { PushEvent } from "../../../lib/mail/pushClient.js";
|
|
8
8
|
|
|
@@ -11,17 +11,18 @@ import type { PushEvent } from "../../../lib/mail/pushClient.js";
|
|
|
11
11
|
const EVENT_MODEL = /^CalendarEvent(Mongo|SQL)$/;
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* The
|
|
14
|
+
* The event a push event says was created, changed or deleted - its uid, and whether it is gone - or `undefined` for anything else the shared push connection
|
|
15
|
+
* delivers.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
* payload has to be safe for a read-only reader of a shared calendar, so it is the busy block (`redacted: true`,
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* The payload is never stored as the event: when somebody creates or changes a private or confidential event, restapi publishes it on the calendar's channel
|
|
18
|
+
* once, for every subscriber, and that one payload has to be safe for a read-only reader of a shared calendar, so it is the busy block (`redacted: true`,
|
|
19
|
+
* title "Busy", no details). Whoever may see the event in full fetches it again by its uid, which answers with as much as they may see - so every created or
|
|
20
|
+
* changed event is handled that way, an ordinary one (an invitation arriving, an edit on another device) included.
|
|
20
21
|
*/
|
|
21
|
-
export function
|
|
22
|
-
if (!EVENT_MODEL.test(event.type) || (event.action !== "create" && event.action !== "update")) {
|
|
22
|
+
export function changedEventOf(event: PushEvent): { uid: string; deleted: boolean } | undefined {
|
|
23
|
+
if (!EVENT_MODEL.test(event.type) || (event.action !== "create" && event.action !== "update" && event.action !== "delete")) {
|
|
23
24
|
return undefined;
|
|
24
25
|
}
|
|
25
|
-
const data = event.data as { uid?: unknown
|
|
26
|
-
return data?.
|
|
26
|
+
const data = event.data as { uid?: unknown } | null | undefined;
|
|
27
|
+
return typeof data?.uid === "string" ? { uid: data.uid, deleted: event.action === "delete" } : undefined;
|
|
27
28
|
}
|
|
@@ -45,6 +45,8 @@ export function useCalendarReminders({ userUid, enabled }: UseCalendarRemindersO
|
|
|
45
45
|
kind: "calendar",
|
|
46
46
|
title: notice.title,
|
|
47
47
|
message: reminderMessage(notice.startDate),
|
|
48
|
+
// Anyone who can invite you can put any address in an event's location: the button says where it goes.
|
|
49
|
+
hint: joinUrl ? `Join Meeting opens ${new URL(joinUrl).host}` : undefined,
|
|
48
50
|
href: CALENDAR_HREF,
|
|
49
51
|
sticky: true,
|
|
50
52
|
actions: [
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
import React, { ReactNode, createContext, useContext } from "react";
|
|
6
|
+
import Alert from "../../../../lib/components/feedback/Alert.js";
|
|
7
|
+
import { isElevationMessage } from "./elevation.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* What `AdminShell` offers the page inside it: sends the browser to auth-server to confirm the user's identity again, then back to this page. `undefined`
|
|
11
|
+
* outside the shell, and in one with no auth-server to send the browser to.
|
|
12
|
+
*/
|
|
13
|
+
export const ReconfirmIdentityContext = createContext<(() => void) | undefined>(undefined);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `Alert` for the failure of an administrator action. When the failure is the server's refusal for want of a recently confirmed identity (see
|
|
17
|
+
* `actionErrorMessage()`), it also offers to confirm it again right there rather than leaving the user to work out that reloading the page does it.
|
|
18
|
+
*/
|
|
19
|
+
export default function ActionAlert({ children }: { children: ReactNode }) {
|
|
20
|
+
const reconfirm = useContext(ReconfirmIdentityContext);
|
|
21
|
+
if (!reconfirm || typeof children !== "string" || !isElevationMessage(children)) {
|
|
22
|
+
return <Alert>{children}</Alert>;
|
|
23
|
+
}
|
|
24
|
+
return (
|
|
25
|
+
<Alert>
|
|
26
|
+
<span className="flex-1">{children}</span>
|
|
27
|
+
<button type="button" className="shrink-0 font-semibold underline" onClick={reconfirm}>
|
|
28
|
+
Confirm identity again
|
|
29
|
+
</button>
|
|
30
|
+
</Alert>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React from "react";
|
|
6
|
-
import Alert from "
|
|
6
|
+
import Alert from "../ActionAlert.js";
|
|
7
7
|
import type { DiagnosticsInformation, DiagnosticsVersions } from "./diagnosticsApi.js";
|
|
8
8
|
import type { DiagnosticsResource } from "./useDiagnosticsResource.js";
|
|
9
9
|
import ComponentsTable from "./ComponentsTable.js";
|
|
@@ -41,13 +41,13 @@ export default function InformationPanel({ versions, plugins, information }: Inf
|
|
|
41
41
|
<SettingsTable
|
|
42
42
|
id="diagnostics-environment"
|
|
43
43
|
title="Environment variables"
|
|
44
|
-
description="The server process's environment.
|
|
44
|
+
description="The server process's environment. Every variable shows its value except those on the server's list of hidden settings, whose value is withheld by the server and never sent."
|
|
45
45
|
settings={information.data.environment ?? []}
|
|
46
46
|
/>
|
|
47
47
|
<SettingsTable
|
|
48
48
|
id="diagnostics-configuration"
|
|
49
49
|
title="Configuration"
|
|
50
|
-
description="The effective settings the server runs with: the environment, saved plugin settings and defaults combined.
|
|
50
|
+
description="The effective settings the server runs with: the environment, saved plugin settings and defaults combined. Settings on the server's list of hidden settings, such as passwords, tokens and keys, are withheld by the server and never sent, and so are the credentials in URLs."
|
|
51
51
|
settings={information.data.configuration ?? []}
|
|
52
52
|
/>
|
|
53
53
|
</>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { HiOutlineExclamationTriangle } from "react-icons/hi2";
|
|
7
7
|
import { getPluginStatus, listPlugins, Plugin, PluginStatus } from "../../../../../lib/admin/pluginsApi.js";
|
|
8
|
-
import Alert from "
|
|
8
|
+
import Alert from "../ActionAlert.js";
|
|
9
9
|
import Badge from "./Badge.js";
|
|
10
10
|
import { NO_VALUE } from "./format.js";
|
|
11
11
|
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
HiOutlineStop,
|
|
16
16
|
HiOutlineTrash,
|
|
17
17
|
} from "react-icons/hi2";
|
|
18
|
-
import Alert from "
|
|
18
|
+
import Alert from "../ActionAlert.js";
|
|
19
19
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
20
20
|
import Badge, { BadgeTone } from "./Badge.js";
|
|
21
21
|
import { SaveFile, timestampedFilename } from "./download.js";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React from "react";
|
|
6
|
-
import Alert from "
|
|
6
|
+
import Alert from "../ActionAlert.js";
|
|
7
7
|
import type { DiagnosticsRuntime } from "./diagnosticsApi.js";
|
|
8
8
|
import type { DiagnosticsResource } from "./useDiagnosticsResource.js";
|
|
9
9
|
import Badge from "./Badge.js";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { HiOutlinePause, HiOutlinePlay } from "react-icons/hi2";
|
|
7
|
-
import Alert from "
|
|
7
|
+
import Alert from "../ActionAlert.js";
|
|
8
8
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
9
9
|
import { formatDateTime } from "./format.js";
|
|
10
10
|
import HostCard from "./HostCard.js";
|
|
@@ -76,8 +76,8 @@ export interface DiagnosticsVersions {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
* One environment variable or configuration setting. A `redacted` one has no `value`: the server withholds every
|
|
80
|
-
*
|
|
79
|
+
* One environment variable or configuration setting. A `redacted` one has no `value`: the server withholds the value of every setting on its
|
|
80
|
+
* hidden list and never sends it, so there is nothing for the client to hide.
|
|
81
81
|
*/
|
|
82
82
|
export interface DiagnosticsSetting {
|
|
83
83
|
name: string;
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { FormEvent, useState } from "react";
|
|
6
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
7
6
|
import { DistributionList, updateDistributionList } from "../../../../../lib/admin/distributionListsApi.js";
|
|
8
|
-
import Alert from "
|
|
7
|
+
import Alert from "../ActionAlert.js";
|
|
9
8
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
9
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
10
10
|
|
|
11
11
|
export interface MemberListCardProps {
|
|
12
12
|
list: DistributionList;
|
|
@@ -45,7 +45,7 @@ export default function MemberListCard({ list, onUpdate }: MemberListCardProps)
|
|
|
45
45
|
setNewAddress("");
|
|
46
46
|
onUpdate(updated);
|
|
47
47
|
} catch (err) {
|
|
48
|
-
setError(err
|
|
48
|
+
setError(actionErrorMessage(err, "Could not add this member."));
|
|
49
49
|
} finally {
|
|
50
50
|
setSaving(false);
|
|
51
51
|
}
|
|
@@ -62,7 +62,7 @@ export default function MemberListCard({ list, onUpdate }: MemberListCardProps)
|
|
|
62
62
|
});
|
|
63
63
|
onUpdate(updated);
|
|
64
64
|
} catch (err) {
|
|
65
|
-
setError(err
|
|
65
|
+
setError(actionErrorMessage(err, "Could not remove this member."));
|
|
66
66
|
} finally {
|
|
67
67
|
setSaving(false);
|
|
68
68
|
}
|
|
@@ -23,6 +23,25 @@ export function isElevationRequired(err: unknown): boolean {
|
|
|
23
23
|
return err instanceof ApiRequestError && err.status === 403 && err.code === ELEVATION_REQUIRED_CODE;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/** What an administrator action says when the server refuses it because the token isn't elevated (any longer). The console confirms the user's identity
|
|
27
|
+
* when a page opens (see `AdminShell`), so reloading is what sends them round auth-server's confirmation again. */
|
|
28
|
+
export const ELEVATION_ACTION_MESSAGE = "This needs you to have recently confirmed your identity. Reload this page to confirm it again, then try once more.";
|
|
29
|
+
|
|
30
|
+
/** Whether `text` is one of the messages that say an action needs the user's identity confirmed again (this one, a plugin's or the diagnostics'). */
|
|
31
|
+
export function isElevationMessage(text: string): boolean {
|
|
32
|
+
return text.includes("recently confirmed your identity");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The text for an administrator action that failed: the elevation message when it only needed the user to confirm their identity (an action the
|
|
36
|
+
* server newly gates behind an elevated token - truncating or deleting a mailbox, a retention policy, an export of another mailbox, approving an
|
|
37
|
+
* erasure), else the server's own message, else `fallback`. */
|
|
38
|
+
export function actionErrorMessage(err: unknown, fallback: string): string {
|
|
39
|
+
if (isElevationRequired(err)) {
|
|
40
|
+
return ELEVATION_ACTION_MESSAGE;
|
|
41
|
+
}
|
|
42
|
+
return err instanceof ApiRequestError ? err.message : fallback;
|
|
43
|
+
}
|
|
44
|
+
|
|
26
45
|
/** auth-server's elevation page: it sends the browser back to `returnTo` once the user has confirmed their
|
|
27
46
|
* identity, or to its own account page if they cancel. */
|
|
28
47
|
export function elevationUrl(authServerUrl: string, returnTo: string): string {
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
-
import React from "react";
|
|
5
|
+
import React, { useEffect, useState } from "react";
|
|
6
|
+
import { escrowPublicKeyFingerprint, normalizeFingerprint } from "../../../../../lib/crypto/escrowKeys.js";
|
|
6
7
|
import { toDatetimeLocal } from "../../../../../lib/util/dateInput.js";
|
|
7
8
|
import { resolveEscrowScopeHolder } from "../../../../../lib/admin/escrowScopesApi.js";
|
|
8
9
|
import FormField from "../../../../../lib/components/forms/FormField.js";
|
|
@@ -52,6 +53,13 @@ export function emptyEscrowScopeKeyAndHoldersValue(): EscrowScopeKeyAndHoldersVa
|
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
/** The message for a key validity date left empty (or not a date), which would otherwise be sent as nothing; `null` when both dates are there. */
|
|
57
|
+
export function keyValidityError(value: Pick<EscrowScopeKeyAndHoldersValue, "notBefore" | "notAfter">): string | null {
|
|
58
|
+
return Number.isNaN(new Date(value.notBefore).getTime()) || Number.isNaN(new Date(value.notAfter).getTime())
|
|
59
|
+
? "The key's validity dates, not before and not after, are both required."
|
|
60
|
+
: null;
|
|
61
|
+
}
|
|
62
|
+
|
|
55
63
|
/** The message shown when an administrator tries to make themselves a holder: whoever configures a scope must not
|
|
56
64
|
* also be able to approve access under it (separation of duties - the server refuses it too). Holders who were
|
|
57
65
|
* already on the scope (`existingHolderUserUids`) aren't treated as newly added. */
|
|
@@ -71,6 +79,33 @@ export default function EscrowScopeKeyAndHoldersFields({ value, onChange, disabl
|
|
|
71
79
|
onChange({ ...value, [key]: next });
|
|
72
80
|
}
|
|
73
81
|
|
|
82
|
+
// What is typed in the required-holders box, which may be empty meanwhile (to be typed over): the value is then 0, which the pages refuse on submit
|
|
83
|
+
// ("between 1 and the number of holders"), rather than snapping back to 1 under the cursor.
|
|
84
|
+
const [requiredText, setRequiredText] = useState(String(value.requiredHolders));
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
setRequiredText((text) => (Number(text) === value.requiredHolders ? text : String(value.requiredHolders)));
|
|
87
|
+
}, [value.requiredHolders]);
|
|
88
|
+
|
|
89
|
+
// What the pasted key's own SHA-256 fingerprint is (`null`: it is no certificate), so the typed one - which the holders compare out of band -
|
|
90
|
+
// can be checked against it. `undefined` until worked out.
|
|
91
|
+
const [actualFingerprint, setActualFingerprint] = useState<string | null | undefined>(undefined);
|
|
92
|
+
const checking = !keyReadOnly && value.publicKey.trim() !== "";
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (!checking) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let cancelled = false;
|
|
98
|
+
setActualFingerprint(undefined);
|
|
99
|
+
void escrowPublicKeyFingerprint(value.publicKey).then((fingerprint) => {
|
|
100
|
+
if (!cancelled) {
|
|
101
|
+
setActualFingerprint(fingerprint);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return () => {
|
|
105
|
+
cancelled = true;
|
|
106
|
+
};
|
|
107
|
+
}, [checking, value.publicKey]);
|
|
108
|
+
|
|
74
109
|
return (
|
|
75
110
|
<>
|
|
76
111
|
<div className="bg-surface border border-border rounded-md p-6">
|
|
@@ -115,6 +150,23 @@ export default function EscrowScopeKeyAndHoldersFields({ value, onChange, disabl
|
|
|
115
150
|
readOnly={keyReadOnly}
|
|
116
151
|
onChange={(e) => set("fingerprint", e.target.value)}
|
|
117
152
|
/>
|
|
153
|
+
{checking && actualFingerprint === null && (
|
|
154
|
+
<p role="alert" className="text-xs text-danger mt-1.5">
|
|
155
|
+
This is not a base64-encoded certificate, so no fingerprint can be checked against it.
|
|
156
|
+
</p>
|
|
157
|
+
)}
|
|
158
|
+
{checking && actualFingerprint && normalizeFingerprint(value.fingerprint) !== actualFingerprint && (
|
|
159
|
+
<p role="alert" className="text-xs text-danger mt-1.5">
|
|
160
|
+
This fingerprint is not the one of the public key above, whose SHA-256 fingerprint is{" "}
|
|
161
|
+
<code className="font-mono break-all">{actualFingerprint}</code>.{" "}
|
|
162
|
+
<button type="button" className="underline font-semibold" disabled={disabled} onClick={() => set("fingerprint", actualFingerprint)}>
|
|
163
|
+
Use it
|
|
164
|
+
</button>
|
|
165
|
+
</p>
|
|
166
|
+
)}
|
|
167
|
+
{checking && actualFingerprint && normalizeFingerprint(value.fingerprint) === actualFingerprint && (
|
|
168
|
+
<p className="text-xs text-text-muted mt-1.5">This is the fingerprint of the public key above.</p>
|
|
169
|
+
)}
|
|
118
170
|
</FormField>
|
|
119
171
|
|
|
120
172
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-x-4">
|
|
@@ -171,9 +223,12 @@ export default function EscrowScopeKeyAndHoldersFields({ value, onChange, disabl
|
|
|
171
223
|
id="requiredHolders"
|
|
172
224
|
type="number"
|
|
173
225
|
className={INPUT_CLASS}
|
|
174
|
-
value={
|
|
226
|
+
value={requiredText}
|
|
175
227
|
disabled={disabled}
|
|
176
|
-
onChange={(e) =>
|
|
228
|
+
onChange={(e) => {
|
|
229
|
+
setRequiredText(e.target.value);
|
|
230
|
+
set("requiredHolders", Number(e.target.value) || 0);
|
|
231
|
+
}}
|
|
177
232
|
/>
|
|
178
233
|
</FormField>
|
|
179
234
|
|
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
recordElevationAttempt,
|
|
49
49
|
} from "../elevation.js";
|
|
50
50
|
import { signOutOfConsole } from "../signOut.js";
|
|
51
|
+
import { ReconfirmIdentityContext } from "../ActionAlert.js";
|
|
51
52
|
import { pluginIcon } from "../../../plugins/pluginIcons.js";
|
|
52
53
|
import { mergePluginNavItems, PluginNav, PluginNavProps } from "../../../plugins/pluginNav.js";
|
|
53
54
|
|
|
@@ -402,7 +403,11 @@ export default function AdminShell({ active, userUid, authServerUrl, pluginNav,
|
|
|
402
403
|
<UserMenu userUid={userUid} authServerUrl={authServerUrl} onSignOut={handleSignOut} showMailLink />
|
|
403
404
|
</header>
|
|
404
405
|
<div id="app-content" className="flex-1">
|
|
405
|
-
<main className="max-w-6xl mx-auto px-6 py-8">
|
|
406
|
+
<main className="max-w-6xl mx-auto px-6 py-8">
|
|
407
|
+
<ReconfirmIdentityContext.Provider value={authServerUrl ? handleRetryElevation : undefined}>
|
|
408
|
+
{children}
|
|
409
|
+
</ReconfirmIdentityContext.Provider>
|
|
410
|
+
</main>
|
|
406
411
|
</div>
|
|
407
412
|
</div>
|
|
408
413
|
</div>
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { FormEvent, useEffect, useRef, useState } from "react";
|
|
6
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
7
6
|
import { eraseLeftoverMailbox } from "../../../../../lib/admin/leftoverMailboxApi.js";
|
|
8
7
|
import { DataSubjectErasureStatus, getErasureRequest } from "../../../../../lib/mail/erasureRequestApi.js";
|
|
9
|
-
import Alert from "
|
|
8
|
+
import Alert from "../ActionAlert.js";
|
|
10
9
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
11
10
|
import Modal from "../../../../../lib/components/overlays/Modal.js";
|
|
12
11
|
import { notify } from "../../../notifications/store.js";
|
|
12
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
13
13
|
|
|
14
14
|
const INPUT_CLASS =
|
|
15
15
|
"w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -86,7 +86,7 @@ export default function EraseLeftoverDataDialog({
|
|
|
86
86
|
setPhase("watching");
|
|
87
87
|
} catch (err) {
|
|
88
88
|
// The server's own words: a legal hold names its matter, a mailbox that exists again says so.
|
|
89
|
-
setError(err
|
|
89
|
+
setError(actionErrorMessage(err, "Could not start the erasure."));
|
|
90
90
|
setPhase("confirm");
|
|
91
91
|
}
|
|
92
92
|
}
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { FormEvent, useEffect, useState } from "react";
|
|
6
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
7
6
|
import { EscrowScope, listEscrowScopes } from "../../../../../lib/admin/escrowScopesApi.js";
|
|
8
7
|
import { Mailbox, updateMailbox } from "../../../../../lib/mail/mailApi.js";
|
|
9
8
|
import { useApiClient } from "../../../../../lib/util/apiClientContext.js";
|
|
10
|
-
import Alert from "
|
|
9
|
+
import Alert from "../ActionAlert.js";
|
|
11
10
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
12
11
|
import Modal from "../../../../../lib/components/overlays/Modal.js";
|
|
12
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
13
13
|
|
|
14
14
|
const SELECT_CLASS =
|
|
15
15
|
"text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -40,7 +40,7 @@ export default function EscrowScopeCard({ mailbox, onUpdate }: EscrowScopeCardPr
|
|
|
40
40
|
useEffect(() => {
|
|
41
41
|
listEscrowScopes({ limit: SCOPE_LIST_LIMIT }, client)
|
|
42
42
|
.then(setScopes)
|
|
43
|
-
.catch((err) => setLoadError(err
|
|
43
|
+
.catch((err) => setLoadError(actionErrorMessage(err, "Could not load escrow scopes.")));
|
|
44
44
|
}, []);
|
|
45
45
|
|
|
46
46
|
// Changing the scope changes who can recover this mailbox's encrypted mail, so it's confirmed first.
|
|
@@ -59,7 +59,7 @@ export default function EscrowScopeCard({ mailbox, onUpdate }: EscrowScopeCardPr
|
|
|
59
59
|
onUpdate(updated);
|
|
60
60
|
setSaved(true);
|
|
61
61
|
} catch (err) {
|
|
62
|
-
setSaveError(err
|
|
62
|
+
setSaveError(actionErrorMessage(err, "Could not save the escrow scope."));
|
|
63
63
|
} finally {
|
|
64
64
|
setSaving(false);
|
|
65
65
|
}
|