@rapidmx/web-client 0.29.0 → 0.31.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 +1 -1
- 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/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 +39 -17
- 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 +58 -8
- 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/SwipeRow.tsx +26 -4
- 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/gestures/useLongPress.ts +138 -0
- 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 +139 -43
- 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 +1 -1
- 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/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 +26 -12
- 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.d.ts +8 -3
- package/dist/apps/shared/components/mail/ConversationList.js +51 -8
- 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/SwipeRow.d.ts +5 -1
- package/dist/apps/shared/components/mail/SwipeRow.js +17 -3
- 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/gestures/useLongPress.d.ts +41 -0
- package/dist/apps/shared/gestures/useLongPress.js +112 -0
- 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 +124 -40
- 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 +13 -2
- package/dist/lib/admin/pluginsApi.js +8 -0
- 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 +21 -2
- 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,6 @@
|
|
|
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 {
|
|
8
7
|
listMailboxAccess,
|
|
9
8
|
MailboxAccessMember,
|
|
@@ -11,10 +10,11 @@ import {
|
|
|
11
10
|
removeMailboxAccess,
|
|
12
11
|
setMailboxAccess,
|
|
13
12
|
} from "../../../../../lib/mail/mailboxAccessApi.js";
|
|
14
|
-
import Alert from "
|
|
13
|
+
import Alert from "../ActionAlert.js";
|
|
15
14
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
16
15
|
import Modal from "../../../../../lib/components/overlays/Modal.js";
|
|
17
16
|
import PrincipalPicker from "../../sharing/PrincipalPicker.js";
|
|
17
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
18
18
|
|
|
19
19
|
const ROLE_LABELS: Record<MailboxAccessRole, string> = { viewer: "Read only", manager: "Full access" };
|
|
20
20
|
|
|
@@ -54,7 +54,7 @@ export default function ShareAccessCard({ mailboxUid, ownerUserUid, currentUserU
|
|
|
54
54
|
setError(null);
|
|
55
55
|
listMailboxAccess(mailboxUid)
|
|
56
56
|
.then(setMembers)
|
|
57
|
-
.catch((err) => setError(err
|
|
57
|
+
.catch((err) => setError(actionErrorMessage(err, "Could not load share settings.")))
|
|
58
58
|
.finally(() => setLoading(false));
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -67,7 +67,7 @@ export default function ShareAccessCard({ mailboxUid, ownerUserUid, currentUserU
|
|
|
67
67
|
await setMailboxAccess(mailboxUid, userUid, "manager");
|
|
68
68
|
reload();
|
|
69
69
|
} catch (err) {
|
|
70
|
-
setError(err
|
|
70
|
+
setError(actionErrorMessage(err, "Could not grant access."));
|
|
71
71
|
} finally {
|
|
72
72
|
setSaving(false);
|
|
73
73
|
}
|
|
@@ -82,7 +82,7 @@ export default function ShareAccessCard({ mailboxUid, ownerUserUid, currentUserU
|
|
|
82
82
|
try {
|
|
83
83
|
await removeMailboxAccess(mailboxUid, stale.userOrRoleId);
|
|
84
84
|
} catch (err) {
|
|
85
|
-
failure = err
|
|
85
|
+
failure = actionErrorMessage(err, "Could not remove the old entry.");
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
reload();
|
|
@@ -102,7 +102,7 @@ export default function ShareAccessCard({ mailboxUid, ownerUserUid, currentUserU
|
|
|
102
102
|
await removeMailboxAccess(mailboxUid, userOrRoleId);
|
|
103
103
|
reload();
|
|
104
104
|
} catch (err) {
|
|
105
|
-
setError(err
|
|
105
|
+
setError(actionErrorMessage(err, "Could not revoke access."));
|
|
106
106
|
} finally {
|
|
107
107
|
setSaving(false);
|
|
108
108
|
}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { ChangeEvent, FormEvent, useRef, useState } from "react";
|
|
6
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
7
6
|
import {
|
|
8
7
|
Branding,
|
|
9
8
|
deleteBrandingIcon,
|
|
@@ -14,9 +13,10 @@ import {
|
|
|
14
13
|
uploadBrandingLogo,
|
|
15
14
|
uploadBrandingStylesheet,
|
|
16
15
|
} from "../../../../../lib/branding/brandingApi.js";
|
|
17
|
-
import Alert from "
|
|
16
|
+
import Alert from "../ActionAlert.js";
|
|
18
17
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
19
18
|
import CopyButton from "../../../../../lib/components/buttons/CopyButton.js";
|
|
19
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
20
20
|
|
|
21
21
|
const INPUT_CLASS =
|
|
22
22
|
"w-full text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -74,10 +74,21 @@ export default function BrandingForm({ branding, onChange, embedded = false }: B
|
|
|
74
74
|
setIconUrlInput(updated.iconUrl ?? "");
|
|
75
75
|
setStylesheetUrlInput(updated.stylesheetUrl ?? "");
|
|
76
76
|
})
|
|
77
|
-
.catch((err) => setError(err
|
|
77
|
+
.catch((err) => setError(actionErrorMessage(err, "Could not update branding.")))
|
|
78
78
|
.finally(() => setAssetBusy(null));
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/** Sets a branding asset to an address the administrator typed. Every visitor's browser loads it - the anonymous booking pages too - so it must be an
|
|
82
|
+
* encrypted (`https:`) address or one on this server (a path), not `http:`, `data:` or `javascript:`. */
|
|
83
|
+
function runUrl(name: string, field: "logoUrl" | "iconUrl" | "stylesheetUrl", value: string) {
|
|
84
|
+
const address = value.trim();
|
|
85
|
+
if (address !== "" && !/^https:\/\/[^\s/]/i.test(address) && !/^\/(?![/\\])/.test(address)) {
|
|
86
|
+
setError("Use an https:// address, or a path on this server, or upload a file instead.");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
void runAsset(name, () => updateBranding({ [field]: address }));
|
|
90
|
+
}
|
|
91
|
+
|
|
81
92
|
function handleLogoFileChange(e: ChangeEvent<HTMLInputElement>) {
|
|
82
93
|
const file = e.target.files?.[0];
|
|
83
94
|
if (!file) return;
|
|
@@ -125,7 +136,7 @@ export default function BrandingForm({ branding, onChange, embedded = false }: B
|
|
|
125
136
|
onChange(updated);
|
|
126
137
|
setSaved(true);
|
|
127
138
|
} catch (err) {
|
|
128
|
-
setError(err
|
|
139
|
+
setError(actionErrorMessage(err, "Could not save branding."));
|
|
129
140
|
} finally {
|
|
130
141
|
setSaving(false);
|
|
131
142
|
}
|
|
@@ -208,7 +219,7 @@ export default function BrandingForm({ branding, onChange, embedded = false }: B
|
|
|
208
219
|
className="!w-auto"
|
|
209
220
|
disabled={assetBusy !== null || logoUrlInput === (branding.logoUrl ?? "")}
|
|
210
221
|
loading={assetBusy === "logo-url"}
|
|
211
|
-
onClick={() =>
|
|
222
|
+
onClick={() => runUrl("logo-url", "logoUrl", logoUrlInput)}
|
|
212
223
|
>
|
|
213
224
|
Set
|
|
214
225
|
</Button>
|
|
@@ -280,7 +291,7 @@ export default function BrandingForm({ branding, onChange, embedded = false }: B
|
|
|
280
291
|
className="!w-auto"
|
|
281
292
|
disabled={assetBusy !== null || iconUrlInput === (branding.iconUrl ?? "")}
|
|
282
293
|
loading={assetBusy === "icon-url"}
|
|
283
|
-
onClick={() =>
|
|
294
|
+
onClick={() => runUrl("icon-url", "iconUrl", iconUrlInput)}
|
|
284
295
|
>
|
|
285
296
|
Set
|
|
286
297
|
</Button>
|
|
@@ -331,6 +342,10 @@ export default function BrandingForm({ branding, onChange, embedded = false }: B
|
|
|
331
342
|
</div>
|
|
332
343
|
<label className="flex flex-col gap-1.5 text-sm">
|
|
333
344
|
<span className="font-semibold">Or use an external stylesheet URL</span>
|
|
345
|
+
<span className="text-xs text-text-muted">
|
|
346
|
+
Every visitor’s browser loads it, and a stylesheet can read what the page shows: only use an address you control, or
|
|
347
|
+
upload the file instead.
|
|
348
|
+
</span>
|
|
334
349
|
<div className="flex gap-2">
|
|
335
350
|
<input
|
|
336
351
|
aria-label="Stylesheet URL"
|
|
@@ -344,9 +359,7 @@ export default function BrandingForm({ branding, onChange, embedded = false }: B
|
|
|
344
359
|
className="!w-auto"
|
|
345
360
|
disabled={assetBusy !== null || stylesheetUrlInput === (branding.stylesheetUrl ?? "")}
|
|
346
361
|
loading={assetBusy === "stylesheet-url"}
|
|
347
|
-
onClick={() =>
|
|
348
|
-
void runAsset("stylesheet-url", () => updateBranding({ stylesheetUrl: stylesheetUrlInput }))
|
|
349
|
-
}
|
|
362
|
+
onClick={() => runUrl("stylesheet-url", "stylesheetUrl", stylesheetUrlInput)}
|
|
350
363
|
>
|
|
351
364
|
Set
|
|
352
365
|
</Button>
|
|
@@ -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 { DnsRecordCheck, Domain, getDnsSetup, getDomain, verifyDomain } from "../../../../../lib/admin/domainsApi.js";
|
|
8
|
-
import Alert from "
|
|
7
|
+
import Alert from "../ActionAlert.js";
|
|
9
8
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
10
9
|
import CopyButton from "../../../../../lib/components/buttons/CopyButton.js";
|
|
10
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
11
11
|
|
|
12
12
|
const RECORD_TYPE_LABELS: Record<DnsRecordCheck["type"], string> = {
|
|
13
13
|
ownership: "Ownership (TXT)",
|
|
@@ -88,7 +88,7 @@ export default function DomainDnsSetup({ uid, onLoaded }: DomainDnsSetupProps) {
|
|
|
88
88
|
setDnsSetup(checks);
|
|
89
89
|
onLoaded?.(d);
|
|
90
90
|
})
|
|
91
|
-
.catch((err) => setError(err
|
|
91
|
+
.catch((err) => setError(actionErrorMessage(err, "Could not load this domain.")))
|
|
92
92
|
.finally(() => setLoading(false));
|
|
93
93
|
}
|
|
94
94
|
|
|
@@ -109,7 +109,7 @@ export default function DomainDnsSetup({ uid, onLoaded }: DomainDnsSetupProps) {
|
|
|
109
109
|
setDnsSetup(checks);
|
|
110
110
|
onLoaded?.(d);
|
|
111
111
|
} catch (err) {
|
|
112
|
-
setVerifyError(err
|
|
112
|
+
setVerifyError(actionErrorMessage(err, "Could not verify this domain."));
|
|
113
113
|
} finally {
|
|
114
114
|
setVerifying(false);
|
|
115
115
|
}
|
|
@@ -3,10 +3,11 @@
|
|
|
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 { EncryptionPolicy, PolicyState, updateEncryptionPolicy } from "../../../../../lib/crypto/keyvaultApi.js";
|
|
8
|
-
import Alert from "
|
|
7
|
+
import Alert from "../ActionAlert.js";
|
|
9
8
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
9
|
+
import Modal from "../../../../../lib/components/overlays/Modal.js";
|
|
10
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
10
11
|
|
|
11
12
|
const SELECT_CLASS =
|
|
12
13
|
"w-full text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -54,8 +55,21 @@ export default function EncryptionPolicyForm({ policy, onChange, onDirtyChange,
|
|
|
54
55
|
const dirty: boolean = TIERS.some((tier) => values[tier.key] !== savedValues[tier.key]);
|
|
55
56
|
useEffect(() => onDirtyChange?.(dirty), [dirty]);
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
// Tiers this save would newly set to "Never encrypt", which switches encryption off for everyone's mail of that kind: confirmed first.
|
|
59
|
+
const [confirmingProhibit, setConfirmingProhibit] = useState<string[] | null>(null);
|
|
60
|
+
|
|
61
|
+
function handleSubmit(e: FormEvent) {
|
|
58
62
|
e.preventDefault();
|
|
63
|
+
const newlyProhibited = TIERS.filter((tier) => values[tier.key] === "prohibited" && savedValues[tier.key] !== "prohibited");
|
|
64
|
+
if (newlyProhibited.length > 0) {
|
|
65
|
+
setConfirmingProhibit(newlyProhibited.map((tier) => tier.label));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
void save();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function save() {
|
|
72
|
+
setConfirmingProhibit(null);
|
|
59
73
|
setError(null);
|
|
60
74
|
setSaved(false);
|
|
61
75
|
setSaving(true);
|
|
@@ -65,7 +79,7 @@ export default function EncryptionPolicyForm({ policy, onChange, onDirtyChange,
|
|
|
65
79
|
setSavedValues(values);
|
|
66
80
|
setSaved(true);
|
|
67
81
|
} catch (err) {
|
|
68
|
-
setError(err
|
|
82
|
+
setError(actionErrorMessage(err, "Could not save the encryption policy."));
|
|
69
83
|
} finally {
|
|
70
84
|
setSaving(false);
|
|
71
85
|
}
|
|
@@ -114,6 +128,21 @@ export default function EncryptionPolicyForm({ policy, onChange, onDirtyChange,
|
|
|
114
128
|
</Button>
|
|
115
129
|
</div>
|
|
116
130
|
</form>
|
|
131
|
+
|
|
132
|
+
<Modal open={confirmingProhibit !== null} onClose={() => setConfirmingProhibit(null)} title="Turn off encryption?">
|
|
133
|
+
<p className="text-sm mb-3">
|
|
134
|
+
Saving sets <strong>{confirmingProhibit?.join(", ")}</strong> to Never encrypt. Nobody on this server can then encrypt
|
|
135
|
+
that mail end to end, whatever they have chosen for themselves, until an administrator allows it again.
|
|
136
|
+
</p>
|
|
137
|
+
<div className="flex gap-3 justify-end">
|
|
138
|
+
<Button type="button" variant="secondary" className="!w-auto" onClick={() => setConfirmingProhibit(null)}>
|
|
139
|
+
Cancel
|
|
140
|
+
</Button>
|
|
141
|
+
<Button type="button" className="!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger" onClick={() => void save()}>
|
|
142
|
+
Save and turn off encryption
|
|
143
|
+
</Button>
|
|
144
|
+
</div>
|
|
145
|
+
</Modal>
|
|
117
146
|
</div>
|
|
118
147
|
);
|
|
119
148
|
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { ReactNode, useEffect, useState } from "react";
|
|
6
|
-
import {
|
|
7
|
-
import Alert from "
|
|
6
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
7
|
+
import Alert from "../ActionAlert.js";
|
|
8
8
|
|
|
9
9
|
export interface LoadedSettingsFormProps<T> {
|
|
10
10
|
load: () => Promise<T>;
|
|
@@ -21,7 +21,7 @@ export default function LoadedSettingsForm<T>({ load, loadErrorMessage, children
|
|
|
21
21
|
useEffect(() => {
|
|
22
22
|
load()
|
|
23
23
|
.then(setValue)
|
|
24
|
-
.catch((err) => setError(err
|
|
24
|
+
.catch((err) => setError(actionErrorMessage(err, loadErrorMessage)));
|
|
25
25
|
}, []);
|
|
26
26
|
|
|
27
27
|
if (error) {
|
|
@@ -3,17 +3,17 @@
|
|
|
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 { getMailboxPolicy } from "../../../../../lib/admin/mailboxPolicyApi.js";
|
|
8
7
|
import { LeftoverConflict, leftoverConflictOf } from "../../../../../lib/admin/leftoverMailboxApi.js";
|
|
9
8
|
import { createMailbox, listMailboxDomains, Mailbox, resolveMailboxOwner, ResolvedPrincipal } from "../../../../../lib/mail/mailApi.js";
|
|
10
9
|
import { describeTimeZone, deviceTimeZone, zoneClock } from "../../../../../lib/util/timeZone.js";
|
|
11
10
|
import { useApiClient } from "../../../../../lib/util/apiClientContext.js";
|
|
12
|
-
import Alert from "
|
|
11
|
+
import Alert from "../ActionAlert.js";
|
|
13
12
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
14
13
|
import FormField from "../../../../../lib/components/forms/FormField.js";
|
|
15
14
|
import PrincipalResolver, { describePerson } from "../../sharing/PrincipalResolver.js";
|
|
16
15
|
import EraseLeftoverDataDialog from "../mailboxes/EraseLeftoverDataDialog.js";
|
|
16
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
17
17
|
|
|
18
18
|
const INPUT_CLASS =
|
|
19
19
|
"w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -98,6 +98,11 @@ export default function MailboxCreateForm({ onCreated, defaults, submitLabel = "
|
|
|
98
98
|
setError("A primary SMTP address is required.");
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
|
+
// With the domain chosen beside it, a local part with an "@" would make `a@b.com@domain`, which the server refuses without saying why.
|
|
102
|
+
if (constrained && /[@@﹫]/.test(localPart)) {
|
|
103
|
+
setError("The part before the domain can't contain \"@\" - choose the domain from the list instead.");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
101
106
|
if (!displayName.trim()) {
|
|
102
107
|
setError("A display name is required.");
|
|
103
108
|
return;
|
|
@@ -143,7 +148,7 @@ export default function MailboxCreateForm({ onCreated, defaults, submitLabel = "
|
|
|
143
148
|
// Not a plain failure: the address is taken by what a deleted mailbox left, which can be erased right here.
|
|
144
149
|
setLeftover({ address, conflict });
|
|
145
150
|
} else {
|
|
146
|
-
setError(err
|
|
151
|
+
setError(actionErrorMessage(err, "Could not create the mailbox."));
|
|
147
152
|
}
|
|
148
153
|
} finally {
|
|
149
154
|
setSaving(false);
|
|
@@ -3,10 +3,10 @@
|
|
|
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 { MailboxPolicy, updateMailboxPolicy } from "../../../../../lib/admin/mailboxPolicyApi.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
|
const INPUT_CLASS =
|
|
12
12
|
"w-full text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
@@ -109,7 +109,7 @@ export default function MailboxPolicyForm({ policy, onChange, onDirtyChange, emb
|
|
|
109
109
|
setBaseline(current);
|
|
110
110
|
setSaved(true);
|
|
111
111
|
} catch (err) {
|
|
112
|
-
setError(err
|
|
112
|
+
setError(actionErrorMessage(err, "Could not save the mailbox policy."));
|
|
113
113
|
} finally {
|
|
114
114
|
setSaving(false);
|
|
115
115
|
}
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { FormEvent, useCallback, useEffect, useId, useRef, useState } from "react";
|
|
6
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
7
6
|
import {
|
|
8
7
|
addPlugin,
|
|
9
8
|
expectedPlanOf,
|
|
10
9
|
getPluginStatus,
|
|
11
10
|
getPluginUpdates,
|
|
11
|
+
isSecretSetting,
|
|
12
|
+
isSecretSettingKey,
|
|
12
13
|
listPluginNamespaces,
|
|
13
14
|
listPlugins,
|
|
14
15
|
lookupPluginPackage,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
PluginPurgeInfo,
|
|
22
23
|
PluginPurgeState,
|
|
23
24
|
PluginRegistryLookup,
|
|
25
|
+
PluginSavedSetting,
|
|
24
26
|
PluginSearchResult,
|
|
25
27
|
PluginSettingDefinition,
|
|
26
28
|
PluginSettingValue,
|
|
@@ -32,11 +34,11 @@ import {
|
|
|
32
34
|
searchPlugins,
|
|
33
35
|
updatePlugin,
|
|
34
36
|
} from "../../../../../lib/admin/pluginsApi.js";
|
|
35
|
-
import Alert from "
|
|
37
|
+
import Alert from "../ActionAlert.js";
|
|
36
38
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
37
39
|
import Modal from "../../../../../lib/components/overlays/Modal.js";
|
|
38
40
|
import { notify } from "../../../notifications/store.js";
|
|
39
|
-
import { isElevationRequired } from "../elevation.js";
|
|
41
|
+
import { actionErrorMessage, isElevationMessage, isElevationRequired } from "../elevation.js";
|
|
40
42
|
import { getAllowPrerelease, setAllowPrerelease } from "./pluginPreferences.js";
|
|
41
43
|
|
|
42
44
|
const INPUT_CLASS =
|
|
@@ -52,7 +54,7 @@ const AFTER_CHANGE_POLL_MS = 2 * 60 * 1000;
|
|
|
52
54
|
const MAX_STATUS_RETRY_MS = 60 * 1000;
|
|
53
55
|
|
|
54
56
|
function errorMessage(err: unknown, fallback: string): string {
|
|
55
|
-
return err
|
|
57
|
+
return actionErrorMessage(err, fallback);
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
/** Runs `action`, resolving why it failed, or `null`. */
|
|
@@ -212,6 +214,8 @@ export default function PluginsManager({ embedded = false }: PluginsManagerProps
|
|
|
212
214
|
const allowPrereleaseRef = useRef(allowPrerelease);
|
|
213
215
|
/** Identifies the latest update check, so a slower earlier one can't replace what it found. */
|
|
214
216
|
const updatesRequest = useRef(0);
|
|
217
|
+
// Set while a bulk action runs, which looks for updates once at its end rather than after each plugin it changes.
|
|
218
|
+
const bulkRunning = useRef(false);
|
|
215
219
|
/** The plugins ticked for a bulk action. */
|
|
216
220
|
const [selected, setSelected] = useState<ReadonlySet<string>>(new Set());
|
|
217
221
|
/** Set while a bulk action runs. */
|
|
@@ -378,7 +382,9 @@ export default function PluginsManager({ embedded = false }: PluginsManagerProps
|
|
|
378
382
|
return [...next].sort((a, b) => a.name.localeCompare(b.name));
|
|
379
383
|
});
|
|
380
384
|
watchRollout();
|
|
381
|
-
|
|
385
|
+
if (!bulkRunning.current) {
|
|
386
|
+
void refreshUpdates();
|
|
387
|
+
}
|
|
382
388
|
}
|
|
383
389
|
|
|
384
390
|
/** Runs an action on a plugin's row (an installed plugin's, or an uninstalled one's), which is busy meanwhile, and
|
|
@@ -579,6 +585,7 @@ export default function PluginsManager({ embedded = false }: PluginsManagerProps
|
|
|
579
585
|
setError(null);
|
|
580
586
|
setRetryEnableUid(null);
|
|
581
587
|
setBulkFailure(null);
|
|
588
|
+
bulkRunning.current = true;
|
|
582
589
|
await track(
|
|
583
590
|
(async () => {
|
|
584
591
|
for (const [index, plugin] of ordered.entries()) {
|
|
@@ -594,7 +601,7 @@ export default function PluginsManager({ embedded = false }: PluginsManagerProps
|
|
|
594
601
|
}
|
|
595
602
|
failed.add(plugin.uid);
|
|
596
603
|
reasons.push(`${plugin.manifest.displayName}: ${problem}`);
|
|
597
|
-
if (problem
|
|
604
|
+
if (isElevationMessage(problem)) {
|
|
598
605
|
for (const untried of ordered.slice(index + 1)) {
|
|
599
606
|
failed.add(untried.uid);
|
|
600
607
|
reasons.push(`${untried.manifest.displayName}: Not attempted.`);
|
|
@@ -604,12 +611,16 @@ export default function PluginsManager({ embedded = false }: PluginsManagerProps
|
|
|
604
611
|
}
|
|
605
612
|
})(),
|
|
606
613
|
);
|
|
614
|
+
bulkRunning.current = false;
|
|
607
615
|
setBulk(null);
|
|
608
616
|
setSelected(failed);
|
|
609
617
|
const succeeded: number = ordered.length - failed.size;
|
|
610
618
|
if (kind === "uninstall" && succeeded > 0) {
|
|
611
619
|
watchRollout();
|
|
612
620
|
}
|
|
621
|
+
if (kind !== "uninstall" && succeeded > 0) {
|
|
622
|
+
void refreshUpdates();
|
|
623
|
+
}
|
|
613
624
|
if (succeeded > 0) {
|
|
614
625
|
notify({
|
|
615
626
|
kind: "success",
|
|
@@ -1675,7 +1686,7 @@ function ConfirmDependenciesModal({
|
|
|
1675
1686
|
}
|
|
1676
1687
|
|
|
1677
1688
|
/** Whether a required select has no value to fall back on, so the form has to pick (and save) one. */
|
|
1678
|
-
function needsSelection(definition: PluginSettingDefinition, saved:
|
|
1689
|
+
function needsSelection(definition: PluginSettingDefinition, saved: PluginSavedSetting | undefined): boolean {
|
|
1679
1690
|
return (
|
|
1680
1691
|
definition.type === "select" &&
|
|
1681
1692
|
!!definition.required &&
|
|
@@ -1693,7 +1704,7 @@ const HOST_PLACEHOLDER = "<host>";
|
|
|
1693
1704
|
* saved yet - nothing, an empty value, or the placeholder itself, which an older server stores when it installs the
|
|
1694
1705
|
* plugin. It's the default with this console's host filled in, and is only stored once the form is saved.
|
|
1695
1706
|
*/
|
|
1696
|
-
function suggestedHost(definition: PluginSettingDefinition, saved:
|
|
1707
|
+
function suggestedHost(definition: PluginSettingDefinition, saved: PluginSavedSetting | undefined): string | undefined {
|
|
1697
1708
|
if (typeof definition.default !== "string" || !definition.default.includes(HOST_PLACEHOLDER) || typeof window === "undefined") {
|
|
1698
1709
|
return undefined;
|
|
1699
1710
|
}
|
|
@@ -1710,12 +1721,12 @@ function configuredValue(definition: PluginSettingDefinition, configured: Plugin
|
|
|
1710
1721
|
}
|
|
1711
1722
|
|
|
1712
1723
|
/** Whether a saved value is absent - nothing, or the empty value a plugin's own "" default is saved as. */
|
|
1713
|
-
function isUnsaved(saved:
|
|
1724
|
+
function isUnsaved(saved: PluginSavedSetting | undefined): boolean {
|
|
1714
1725
|
return saved === undefined || saved === "";
|
|
1715
1726
|
}
|
|
1716
1727
|
|
|
1717
1728
|
/** The suggestion for the host, unless the deployment configures the setting, which is offered instead. */
|
|
1718
|
-
function hostSuggestion(definition: PluginSettingDefinition, saved:
|
|
1729
|
+
function hostSuggestion(definition: PluginSettingDefinition, saved: PluginSavedSetting | undefined, configured: PluginConfiguredSetting | undefined): string | undefined {
|
|
1719
1730
|
return configured ? undefined : suggestedHost(definition, saved);
|
|
1720
1731
|
}
|
|
1721
1732
|
|
|
@@ -1724,9 +1735,13 @@ function hostSuggestion(definition: PluginSettingDefinition, saved: PluginSettin
|
|
|
1724
1735
|
* option. Kept as a string for text inputs. */
|
|
1725
1736
|
function initialValue(
|
|
1726
1737
|
definition: PluginSettingDefinition,
|
|
1727
|
-
saved:
|
|
1738
|
+
saved: PluginSavedSetting | undefined,
|
|
1728
1739
|
configured?: PluginConfiguredSetting,
|
|
1729
1740
|
): PluginSettingValue | "" {
|
|
1741
|
+
if (isSecretSetting(saved)) {
|
|
1742
|
+
// Write-only: the saved secret never reaches the browser, so the field starts empty. Left empty, the placeholder is sent back and the secret stays.
|
|
1743
|
+
return "";
|
|
1744
|
+
}
|
|
1730
1745
|
if (configured && isUnsaved(saved)) {
|
|
1731
1746
|
return configuredValue(definition, configured);
|
|
1732
1747
|
}
|
|
@@ -1768,12 +1783,12 @@ function SettingsModal({ plugin, onClose, onSaved }: { plugin: Plugin; onClose:
|
|
|
1768
1783
|
// A required select with nothing to fall back on, and a setting offering this console's host, are sent as the
|
|
1769
1784
|
// form shows them. What the deployment configures is never saved as a copy, which would outrank it when it changes
|
|
1770
1785
|
// - a field that shows it is sent as nothing.
|
|
1771
|
-
const settings: Record<string,
|
|
1786
|
+
const settings: Record<string, PluginSavedSetting | null> = {};
|
|
1772
1787
|
let changed = false;
|
|
1773
1788
|
for (const definition of definitions) {
|
|
1774
1789
|
const key: string = definition.key;
|
|
1775
1790
|
const configured: PluginConfiguredSetting | undefined = plugin.configured?.[key];
|
|
1776
|
-
const saved:
|
|
1791
|
+
const saved: PluginSavedSetting | undefined = plugin.settings[key];
|
|
1777
1792
|
const value = values[key];
|
|
1778
1793
|
if (resetting && value === initialValue(definition, undefined, configured)) {
|
|
1779
1794
|
// What a fresh install saves: only a default naming this console's host has to be, for the plugin to work.
|
|
@@ -1820,6 +1835,7 @@ function SettingsModal({ plugin, onClose, onSaved }: { plugin: Plugin; onClose:
|
|
|
1820
1835
|
definition={definition}
|
|
1821
1836
|
value={values[definition.key]}
|
|
1822
1837
|
configured={plugin.configured?.[definition.key]}
|
|
1838
|
+
secretSaved={!resetting && isSecretSetting(plugin.settings[definition.key])}
|
|
1823
1839
|
onChange={(value) => setValues((prev) => ({ ...prev, [definition.key]: value }))}
|
|
1824
1840
|
/>
|
|
1825
1841
|
))}
|
|
@@ -1852,11 +1868,14 @@ function SettingField({
|
|
|
1852
1868
|
definition,
|
|
1853
1869
|
value,
|
|
1854
1870
|
configured,
|
|
1871
|
+
secretSaved,
|
|
1855
1872
|
onChange,
|
|
1856
1873
|
}: {
|
|
1857
1874
|
definition: PluginSettingDefinition;
|
|
1858
1875
|
value: PluginSettingValue | "";
|
|
1859
1876
|
configured?: PluginConfiguredSetting;
|
|
1877
|
+
/** A secret is saved: the server never sends it, so the field is write-only - typing replaces it, leaving it empty keeps it. */
|
|
1878
|
+
secretSaved: boolean;
|
|
1860
1879
|
onChange: (value: PluginSettingValue | "") => void;
|
|
1861
1880
|
}) {
|
|
1862
1881
|
// What applies without a saved value; one saved here replaces it, and Reset goes back to it.
|
|
@@ -1865,8 +1884,10 @@ function SettingField({
|
|
|
1865
1884
|
This deployment's configuration provides {configured.secret ? "a value" : "the value shown"} unless one is saved here.
|
|
1866
1885
|
</span>
|
|
1867
1886
|
);
|
|
1868
|
-
// A secret's value never reaches the browser, so the field only says that it is set
|
|
1869
|
-
|
|
1887
|
+
// A secret's value never reaches the browser, so the field only says that it is set - and never shows what is typed, or offers it to the
|
|
1888
|
+
// browser's form fill.
|
|
1889
|
+
const secretField: boolean = definition.type === "string" && (isSecretSettingKey(definition.key) || secretSaved || !!configured?.secret);
|
|
1890
|
+
const placeholder: string | undefined = secretSaved ? "Saved - type a new value to replace it" : configured?.secret ? "Set by this deployment" : undefined;
|
|
1870
1891
|
const help = (
|
|
1871
1892
|
<>
|
|
1872
1893
|
{definition.help && <span className="text-xs text-text-muted">{definition.help}</span>}
|
|
@@ -1901,10 +1922,11 @@ function SettingField({
|
|
|
1901
1922
|
<input
|
|
1902
1923
|
aria-label={definition.label}
|
|
1903
1924
|
className={INPUT_CLASS}
|
|
1904
|
-
type={definition.type === "number" ? "number" : "text"}
|
|
1925
|
+
type={definition.type === "number" ? "number" : secretField ? "password" : "text"}
|
|
1926
|
+
autoComplete={secretField ? "new-password" : undefined}
|
|
1905
1927
|
min={definition.min}
|
|
1906
1928
|
max={definition.max}
|
|
1907
|
-
required={definition.required && !configured}
|
|
1929
|
+
required={definition.required && !configured && !secretSaved}
|
|
1908
1930
|
placeholder={placeholder}
|
|
1909
1931
|
value={String(value)}
|
|
1910
1932
|
onChange={(e) => onChange(e.target.value)}
|
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React, { FormEvent, useEffect, useState } from "react";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
6
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
7
|
+
import {
|
|
8
|
+
MIN_AUDIT_LOG_RETENTION_DAYS,
|
|
9
|
+
MIN_MESSAGE_RETENTION_DAYS,
|
|
10
|
+
RetentionPolicy,
|
|
11
|
+
RetentionPolicyUpdate,
|
|
12
|
+
updateRetentionPolicy,
|
|
13
|
+
} from "../../../../../lib/admin/retentionPolicyApi.js";
|
|
14
|
+
import Alert from "../ActionAlert.js";
|
|
9
15
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
10
16
|
import Modal from "../../../../../lib/components/overlays/Modal.js";
|
|
11
17
|
|
|
@@ -49,14 +55,21 @@ export default function RetentionPolicyForm({
|
|
|
49
55
|
const [savedMessageDays, setSavedMessageDays] = useState(policy.messageRetentionDays);
|
|
50
56
|
const [savedAuditDays, setSavedAuditDays] = useState(policy.auditLogRetentionDays);
|
|
51
57
|
const [pendingPatch, setPendingPatch] = useState<RetentionPolicyUpdate | null>(null);
|
|
58
|
+
// A period that is still what is stored is neither sent nor held to the floor: it may have been stored before the floor was set.
|
|
59
|
+
const messageChanged = messageRetentionDays !== (savedMessageDays?.toString() ?? "");
|
|
60
|
+
const auditChanged = auditLogRetentionDays !== (savedAuditDays?.toString() ?? "");
|
|
52
61
|
|
|
53
62
|
function handleSubmit(e: FormEvent) {
|
|
54
63
|
e.preventDefault();
|
|
55
|
-
// A blank field is sent as `null`, which clears a configured age back to no automatic purge.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
// A blank field is sent as `null`, which clears a configured age back to no automatic purge. Only a field that was changed is sent: one left as
|
|
65
|
+
// it is would be refused by a floor that came in after it was stored, and so keep the other from being saved.
|
|
66
|
+
const patch: RetentionPolicyUpdate = {};
|
|
67
|
+
if (messageChanged) {
|
|
68
|
+
patch.messageRetentionDays = messageRetentionDays.trim() === "" ? null : Number(messageRetentionDays);
|
|
69
|
+
}
|
|
70
|
+
if (auditChanged) {
|
|
71
|
+
patch.auditLogRetentionDays = auditLogRetentionDays.trim() === "" ? null : Number(auditLogRetentionDays);
|
|
72
|
+
}
|
|
60
73
|
if (
|
|
61
74
|
shortensRetention(patch.messageRetentionDays, savedMessageDays) ||
|
|
62
75
|
shortensRetention(patch.auditLogRetentionDays, savedAuditDays)
|
|
@@ -80,7 +93,7 @@ export default function RetentionPolicyForm({
|
|
|
80
93
|
setSavedSnapshot(snapshot);
|
|
81
94
|
setSaved(true);
|
|
82
95
|
} catch (err) {
|
|
83
|
-
setError(err
|
|
96
|
+
setError(actionErrorMessage(err, "Could not save the retention policy."));
|
|
84
97
|
} finally {
|
|
85
98
|
setSaving(false);
|
|
86
99
|
}
|
|
@@ -109,14 +122,14 @@ export default function RetentionPolicyForm({
|
|
|
109
122
|
<input
|
|
110
123
|
aria-label="Message retention (days)"
|
|
111
124
|
type="number"
|
|
112
|
-
min={
|
|
125
|
+
min={messageChanged ? MIN_MESSAGE_RETENTION_DAYS : undefined}
|
|
113
126
|
className={INPUT_CLASS}
|
|
114
127
|
value={messageRetentionDays}
|
|
115
128
|
onChange={(e) => setMessageRetentionDays(e.target.value)}
|
|
116
129
|
placeholder="No automatic purge"
|
|
117
130
|
/>
|
|
118
131
|
<span className="text-xs text-text-muted">
|
|
119
|
-
Applies to any message, in any folder, regardless of age.
|
|
132
|
+
Applies to any message, in any folder, regardless of age. Cannot be set below {MIN_MESSAGE_RETENTION_DAYS} days.
|
|
120
133
|
</span>
|
|
121
134
|
</label>
|
|
122
135
|
<label className="flex flex-col gap-1.5 text-sm">
|
|
@@ -124,7 +137,7 @@ export default function RetentionPolicyForm({
|
|
|
124
137
|
<input
|
|
125
138
|
aria-label="Audit log retention (days)"
|
|
126
139
|
type="number"
|
|
127
|
-
min={MIN_AUDIT_LOG_RETENTION_DAYS}
|
|
140
|
+
min={auditChanged ? MIN_AUDIT_LOG_RETENTION_DAYS : undefined}
|
|
128
141
|
className={INPUT_CLASS}
|
|
129
142
|
value={auditLogRetentionDays}
|
|
130
143
|
onChange={(e) => setAuditLogRetentionDays(e.target.value)}
|