@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
|
@@ -4,13 +4,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
import { useEffect, useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
8
7
|
import { listEscrowScopes } from "../../../../../lib/admin/escrowScopesApi.js";
|
|
9
8
|
import { updateMailbox } from "../../../../../lib/mail/mailApi.js";
|
|
10
9
|
import { useApiClient } from "../../../../../lib/util/apiClientContext.js";
|
|
11
|
-
import Alert from "
|
|
10
|
+
import Alert from "../ActionAlert.js";
|
|
12
11
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
13
12
|
import Modal from "../../../../../lib/components/overlays/Modal.js";
|
|
13
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
14
14
|
const SELECT_CLASS = "text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
15
15
|
/** Enough for any realistic deployment's scopes in one list. */
|
|
16
16
|
const SCOPE_LIST_LIMIT = 200;
|
|
@@ -30,7 +30,7 @@ export default function EscrowScopeCard({ mailbox, onUpdate }) {
|
|
|
30
30
|
useEffect(() => {
|
|
31
31
|
listEscrowScopes({ limit: SCOPE_LIST_LIMIT }, client)
|
|
32
32
|
.then(setScopes)
|
|
33
|
-
.catch((err) => setLoadError(err
|
|
33
|
+
.catch((err) => setLoadError(actionErrorMessage(err, "Could not load escrow scopes.")));
|
|
34
34
|
}, []);
|
|
35
35
|
// Changing the scope changes who can recover this mailbox's encrypted mail, so it's confirmed first.
|
|
36
36
|
function handleSubmit(e) {
|
|
@@ -48,7 +48,7 @@ export default function EscrowScopeCard({ mailbox, onUpdate }) {
|
|
|
48
48
|
setSaved(true);
|
|
49
49
|
}
|
|
50
50
|
catch (err) {
|
|
51
|
-
setSaveError(err
|
|
51
|
+
setSaveError(actionErrorMessage(err, "Could not save the escrow scope."));
|
|
52
52
|
}
|
|
53
53
|
finally {
|
|
54
54
|
setSaving(false);
|
|
@@ -4,11 +4,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
import { useEffect, useRef, useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
8
7
|
import { listLeftoverMailboxes } from "../../../../../lib/admin/leftoverMailboxApi.js";
|
|
9
|
-
import Alert from "
|
|
8
|
+
import Alert from "../ActionAlert.js";
|
|
10
9
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
11
10
|
import EraseLeftoverDataDialog from "./EraseLeftoverDataDialog.js";
|
|
11
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
12
12
|
/** How many deleted mailboxes one request asks for. */
|
|
13
13
|
const PAGE_SIZE = 50;
|
|
14
14
|
/** How often the list is refreshed while an erasure is under way. */
|
|
@@ -45,7 +45,7 @@ export default function LeftoverMailboxesSection({ refreshIntervalMs = LEFTOVER_
|
|
|
45
45
|
}
|
|
46
46
|
catch (err) {
|
|
47
47
|
if (current === generation.current) {
|
|
48
|
-
setError(err
|
|
48
|
+
setError(actionErrorMessage(err, "Could not check for deleted mailboxes with remaining data."));
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -61,7 +61,7 @@ export default function LeftoverMailboxesSection({ refreshIntervalMs = LEFTOVER_
|
|
|
61
61
|
}
|
|
62
62
|
catch (err) {
|
|
63
63
|
if (current === generation.current) {
|
|
64
|
-
setError(err
|
|
64
|
+
setError(actionErrorMessage(err, "Could not load more deleted mailboxes."));
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
finally {
|
|
@@ -4,11 +4,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
import { useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
8
7
|
import { 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";
|
|
11
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
12
12
|
const INPUT_CLASS = "w-full text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
13
13
|
const SELECT_CLASS = "text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
14
14
|
/**
|
|
@@ -51,7 +51,7 @@ export default function ResourceSettingsCard({ mailbox, onUpdate }) {
|
|
|
51
51
|
setSaved(true);
|
|
52
52
|
}
|
|
53
53
|
catch (err) {
|
|
54
|
-
setError(err
|
|
54
|
+
setError(actionErrorMessage(err, "Could not save resource settings."));
|
|
55
55
|
}
|
|
56
56
|
finally {
|
|
57
57
|
setSaving(false);
|
|
@@ -4,12 +4,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
import { useEffect, useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
8
7
|
import { listMailboxAccess, removeMailboxAccess, setMailboxAccess, } from "../../../../../lib/mail/mailboxAccessApi.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 PrincipalPicker from "../../sharing/PrincipalPicker.js";
|
|
12
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
13
13
|
const ROLE_LABELS = { viewer: "Read only", manager: "Full access" };
|
|
14
14
|
function roleLabel(member) {
|
|
15
15
|
if (member.role === "viewer")
|
|
@@ -38,7 +38,7 @@ export default function ShareAccessCard({ mailboxUid, ownerUserUid, currentUserU
|
|
|
38
38
|
setError(null);
|
|
39
39
|
listMailboxAccess(mailboxUid)
|
|
40
40
|
.then(setMembers)
|
|
41
|
-
.catch((err) => setError(err
|
|
41
|
+
.catch((err) => setError(actionErrorMessage(err, "Could not load share settings.")))
|
|
42
42
|
.finally(() => setLoading(false));
|
|
43
43
|
}
|
|
44
44
|
useEffect(reload, [mailboxUid]);
|
|
@@ -50,7 +50,7 @@ export default function ShareAccessCard({ mailboxUid, ownerUserUid, currentUserU
|
|
|
50
50
|
reload();
|
|
51
51
|
}
|
|
52
52
|
catch (err) {
|
|
53
|
-
setError(err
|
|
53
|
+
setError(actionErrorMessage(err, "Could not grant access."));
|
|
54
54
|
}
|
|
55
55
|
finally {
|
|
56
56
|
setSaving(false);
|
|
@@ -66,7 +66,7 @@ export default function ShareAccessCard({ mailboxUid, ownerUserUid, currentUserU
|
|
|
66
66
|
await removeMailboxAccess(mailboxUid, stale.userOrRoleId);
|
|
67
67
|
}
|
|
68
68
|
catch (err) {
|
|
69
|
-
failure = err
|
|
69
|
+
failure = actionErrorMessage(err, "Could not remove the old entry.");
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
reload();
|
|
@@ -86,7 +86,7 @@ export default function ShareAccessCard({ mailboxUid, ownerUserUid, currentUserU
|
|
|
86
86
|
reload();
|
|
87
87
|
}
|
|
88
88
|
catch (err) {
|
|
89
|
-
setError(err
|
|
89
|
+
setError(actionErrorMessage(err, "Could not revoke access."));
|
|
90
90
|
}
|
|
91
91
|
finally {
|
|
92
92
|
setSaving(false);
|
|
@@ -4,11 +4,11 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
import { useRef, useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
8
7
|
import { deleteBrandingIcon, deleteBrandingLogo, deleteBrandingStylesheet, updateBranding, uploadBrandingIcon, uploadBrandingLogo, uploadBrandingStylesheet, } from "../../../../../lib/branding/brandingApi.js";
|
|
9
|
-
import Alert from "
|
|
8
|
+
import Alert from "../ActionAlert.js";
|
|
10
9
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
11
10
|
import CopyButton from "../../../../../lib/components/buttons/CopyButton.js";
|
|
11
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
12
12
|
const INPUT_CLASS = "w-full text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
13
13
|
const MAX_UPLOAD_BYTES = 5 * 1024 * 1024;
|
|
14
14
|
/** A header that uses both variables - the snippet the Branding page offers to copy. */
|
|
@@ -48,9 +48,19 @@ export default function BrandingForm({ branding, onChange, embedded = false }) {
|
|
|
48
48
|
setIconUrlInput(updated.iconUrl ?? "");
|
|
49
49
|
setStylesheetUrlInput(updated.stylesheetUrl ?? "");
|
|
50
50
|
})
|
|
51
|
-
.catch((err) => setError(err
|
|
51
|
+
.catch((err) => setError(actionErrorMessage(err, "Could not update branding.")))
|
|
52
52
|
.finally(() => setAssetBusy(null));
|
|
53
53
|
}
|
|
54
|
+
/** 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
|
|
55
|
+
* encrypted (`https:`) address or one on this server (a path), not `http:`, `data:` or `javascript:`. */
|
|
56
|
+
function runUrl(name, field, value) {
|
|
57
|
+
const address = value.trim();
|
|
58
|
+
if (address !== "" && !/^https:\/\/[^\s/]/i.test(address) && !/^\/(?![/\\])/.test(address)) {
|
|
59
|
+
setError("Use an https:// address, or a path on this server, or upload a file instead.");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
void runAsset(name, () => updateBranding({ [field]: address }));
|
|
63
|
+
}
|
|
54
64
|
function handleLogoFileChange(e) {
|
|
55
65
|
const file = e.target.files?.[0];
|
|
56
66
|
if (!file)
|
|
@@ -104,7 +114,7 @@ export default function BrandingForm({ branding, onChange, embedded = false }) {
|
|
|
104
114
|
setSaved(true);
|
|
105
115
|
}
|
|
106
116
|
catch (err) {
|
|
107
|
-
setError(err
|
|
117
|
+
setError(actionErrorMessage(err, "Could not save branding."));
|
|
108
118
|
}
|
|
109
119
|
finally {
|
|
110
120
|
setSaving(false);
|
|
@@ -113,11 +123,11 @@ export default function BrandingForm({ branding, onChange, embedded = false }) {
|
|
|
113
123
|
return (_jsxs("div", { className: "max-w-2xl", children: [!embedded && (_jsxs(_Fragment, { children: [_jsx("h1", { className: "text-xl font-bold uppercase tracking-wide mb-1", children: "Branding" }), _jsx("p", { className: "text-sm text-text-muted mb-5", children: "Customize the logo, nav-header icon, product name, and chrome shown to every visitor of the webmail and admin console \u2014 including anonymous booking-page visitors. The header appears on the webmail and public pages, not in the admin console; the footer appears in both." })] })), error && _jsx(Alert, { children: error }), saved && !error && _jsx("div", { className: "mb-4 text-sm text-success font-medium", children: "Saved." }), _jsxs("div", { className: "flex flex-col gap-6", children: [_jsxs("section", { className: "flex flex-col gap-3", children: [_jsx(SectionHeading, { className: "text-sm font-bold uppercase tracking-wide text-text-muted", children: "Logo" }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("img", { src: branding.logoUrl || "/images/logo.svg", alt: "Current logo", width: "64", height: "64", className: "border border-border rounded-sm bg-surface-alt p-1" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", loading: assetBusy === "logo-upload", disabled: assetBusy !== null, onClick: () => logoFileRef.current?.click(), children: "Upload logo" }), _jsx("input", { ref: logoFileRef, type: "file", accept: RASTER_IMAGE_ACCEPT, "aria-label": "Upload logo", className: "hidden", onChange: handleLogoFileChange }), branding.logoUrl && (_jsx(Button, { type: "button", variant: "text", disabled: assetBusy !== null, loading: assetBusy === "logo-delete", onClick: () => void runAsset("logo-delete", async () => {
|
|
114
124
|
await deleteBrandingLogo();
|
|
115
125
|
return { ...branding, logoUrl: undefined };
|
|
116
|
-
}), children: "Remove logo" }))] })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external image URL" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Logo URL", className: INPUT_CLASS, value: logoUrlInput, onChange: (e) => setLogoUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || logoUrlInput === (branding.logoUrl ?? ""), loading: assetBusy === "logo-url", onClick: () =>
|
|
126
|
+
}), children: "Remove logo" }))] })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external image URL" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Logo URL", className: INPUT_CLASS, value: logoUrlInput, onChange: (e) => setLogoUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || logoUrlInput === (branding.logoUrl ?? ""), loading: assetBusy === "logo-url", onClick: () => runUrl("logo-url", "logoUrl", logoUrlInput), children: "Set" })] })] })] }), _jsxs("section", { className: "flex flex-col gap-3", children: [_jsx(SectionHeading, { className: "text-sm font-bold uppercase tracking-wide text-text-muted", children: "Icon" }), _jsx("p", { className: "text-xs text-text-muted -mt-1", children: "A compact mark for navigation headers, independent of the full logo above. Falls back to the logo, then a default asset, when not set." }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("img", { src: branding.iconUrl || branding.logoUrl || "/images/logo.svg", alt: "Current icon", width: "64", height: "64", className: "border border-border rounded-sm bg-surface-alt p-1" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", loading: assetBusy === "icon-upload", disabled: assetBusy !== null, onClick: () => iconFileRef.current?.click(), children: "Upload icon" }), _jsx("input", { ref: iconFileRef, type: "file", accept: RASTER_IMAGE_ACCEPT, "aria-label": "Upload icon", className: "hidden", onChange: handleIconFileChange }), branding.iconUrl && (_jsx(Button, { type: "button", variant: "text", disabled: assetBusy !== null, loading: assetBusy === "icon-delete", onClick: () => void runAsset("icon-delete", async () => {
|
|
117
127
|
await deleteBrandingIcon();
|
|
118
128
|
return { ...branding, iconUrl: undefined };
|
|
119
|
-
}), children: "Remove icon" }))] })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external image URL" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Icon URL", className: INPUT_CLASS, value: iconUrlInput, onChange: (e) => setIconUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || iconUrlInput === (branding.iconUrl ?? ""), loading: assetBusy === "icon-url", onClick: () =>
|
|
129
|
+
}), children: "Remove icon" }))] })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external image URL" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Icon URL", className: INPUT_CLASS, value: iconUrlInput, onChange: (e) => setIconUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || iconUrlInput === (branding.iconUrl ?? ""), loading: assetBusy === "icon-url", onClick: () => runUrl("icon-url", "iconUrl", iconUrlInput), children: "Set" })] })] })] }), _jsxs("section", { className: "flex flex-col gap-3", children: [_jsx(SectionHeading, { className: "text-sm font-bold uppercase tracking-wide text-text-muted", children: "Custom stylesheet" }), _jsx("div", { className: "flex items-center gap-3", children: _jsx("span", { className: "text-sm text-text-muted truncate", children: branding.stylesheetUrl || "None configured" }) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", loading: assetBusy === "stylesheet-upload", disabled: assetBusy !== null, onClick: () => stylesheetFileRef.current?.click(), children: "Upload CSS file" }), _jsx("input", { ref: stylesheetFileRef, type: "file", accept: "text/css,.css", "aria-label": "Upload stylesheet", className: "hidden", onChange: handleStylesheetFileChange }), branding.stylesheetUrl && (_jsx(Button, { type: "button", variant: "text", disabled: assetBusy !== null, loading: assetBusy === "stylesheet-delete", onClick: () => void runAsset("stylesheet-delete", async () => {
|
|
120
130
|
await deleteBrandingStylesheet();
|
|
121
131
|
return { ...branding, stylesheetUrl: undefined };
|
|
122
|
-
}), children: "Remove stylesheet" }))] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external stylesheet URL" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Stylesheet URL", className: INPUT_CLASS, value: stylesheetUrlInput, onChange: (e) => setStylesheetUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || stylesheetUrlInput === (branding.stylesheetUrl ?? ""), loading: assetBusy === "stylesheet-url", onClick: () =>
|
|
132
|
+
}), children: "Remove stylesheet" }))] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Or use an external stylesheet URL" }), _jsx("span", { className: "text-xs text-text-muted", children: "Every visitor\u2019s browser loads it, and a stylesheet can read what the page shows: only use an address you control, or upload the file instead." }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { "aria-label": "Stylesheet URL", className: INPUT_CLASS, value: stylesheetUrlInput, onChange: (e) => setStylesheetUrlInput(e.target.value) }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: assetBusy !== null || stylesheetUrlInput === (branding.stylesheetUrl ?? ""), loading: assetBusy === "stylesheet-url", onClick: () => runUrl("stylesheet-url", "stylesheetUrl", stylesheetUrlInput), children: "Set" })] })] })] }), _jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4 pt-2 border-t border-border", children: [_jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Company name" }), _jsx("input", { "aria-label": "Company name", className: INPUT_CLASS, value: companyName, onChange: (e) => setCompanyName(e.target.value) })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Product title" }), _jsx("input", { "aria-label": "Product title", className: INPUT_CLASS, value: title, onChange: (e) => setTitle(e.target.value) }), _jsx("span", { className: "text-xs text-text-muted", children: "Shown as the browser tab title." })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Header HTML (optional)" }), _jsx("textarea", { "aria-label": "Header HTML", className: `${INPUT_CLASS} font-mono`, rows: 3, value: headerHtml, onChange: (e) => setHeaderHtml(e.target.value) }), _jsxs("span", { className: "text-xs text-text-muted", children: ["A header replaces the app's own title bar and the icon at the top of the icon rail, and stays at the top of the window. Write", " ", _jsx("code", { children: "{USER_MENU}" }), " where the account menu (avatar, Settings, Sign Out) should go, and ", _jsx("code", { children: "{APP_TITLE}" }), " ", "where the name of the app on screen should show. Both work in the footer too. ", _jsx("code", { children: "{USER_MENU}" }), " is replaced once (the header's, else the footer's); without it the menu sits in a small cell at the right end of the header, so it is never lost. Put it where your styles don't hide it on a phone."] })] }), _jsxs("div", { className: "flex flex-col gap-1.5 text-sm", role: "group", "aria-label": "Header example", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { className: "font-semibold", children: "Example header" }), _jsx(CopyButton, { value: HEADER_HTML_EXAMPLE, label: "Copy the example header HTML" })] }), _jsx("pre", { className: "overflow-x-auto rounded-sm border border-border bg-surface-alt p-3 font-mono text-xs text-text", children: HEADER_HTML_EXAMPLE })] }), _jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Footer HTML (optional)" }), _jsx("textarea", { "aria-label": "Footer HTML", className: `${INPUT_CLASS} font-mono`, rows: 3, value: footerHtml, onChange: (e) => setFooterHtml(e.target.value) }), _jsxs("span", { className: "text-xs text-text-muted", children: [_jsx("code", { children: "{APP_TITLE}" }), " works here as well, and ", _jsx("code", { children: "{USER_MENU}" }), " takes the account menu (opening upward) when the header has none."] })] }), _jsx("div", { children: _jsx(Button, { type: "submit", loading: saving, disabled: saving, className: "!w-auto", children: "Save" }) })] })] })] }));
|
|
123
133
|
}
|
|
@@ -4,11 +4,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
import { useEffect, useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
8
7
|
import { getDnsSetup, getDomain, verifyDomain } from "../../../../../lib/admin/domainsApi.js";
|
|
9
|
-
import Alert from "
|
|
8
|
+
import Alert from "../ActionAlert.js";
|
|
10
9
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
11
10
|
import CopyButton from "../../../../../lib/components/buttons/CopyButton.js";
|
|
11
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
12
12
|
const RECORD_TYPE_LABELS = {
|
|
13
13
|
ownership: "Ownership (TXT)",
|
|
14
14
|
mx: "MX",
|
|
@@ -68,7 +68,7 @@ export default function DomainDnsSetup({ uid, onLoaded }) {
|
|
|
68
68
|
setDnsSetup(checks);
|
|
69
69
|
onLoaded?.(d);
|
|
70
70
|
})
|
|
71
|
-
.catch((err) => setError(err
|
|
71
|
+
.catch((err) => setError(actionErrorMessage(err, "Could not load this domain.")))
|
|
72
72
|
.finally(() => setLoading(false));
|
|
73
73
|
}
|
|
74
74
|
useEffect(() => {
|
|
@@ -88,7 +88,7 @@ export default function DomainDnsSetup({ uid, onLoaded }) {
|
|
|
88
88
|
onLoaded?.(d);
|
|
89
89
|
}
|
|
90
90
|
catch (err) {
|
|
91
|
-
setVerifyError(err
|
|
91
|
+
setVerifyError(actionErrorMessage(err, "Could not verify this domain."));
|
|
92
92
|
}
|
|
93
93
|
finally {
|
|
94
94
|
setVerifying(false);
|
|
@@ -4,10 +4,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
import { useEffect, useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
8
7
|
import { updateEncryptionPolicy } from "../../../../../lib/crypto/keyvaultApi.js";
|
|
9
|
-
import Alert from "
|
|
8
|
+
import Alert from "../ActionAlert.js";
|
|
10
9
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
10
|
+
import Modal from "../../../../../lib/components/overlays/Modal.js";
|
|
11
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
11
12
|
const SELECT_CLASS = "w-full text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
12
13
|
const TIERS = [
|
|
13
14
|
{ key: "encryptSameOrg", label: "Mail within this server", help: "Messages between mailboxes hosted on this server." },
|
|
@@ -37,8 +38,19 @@ export default function EncryptionPolicyForm({ policy, onChange, onDirtyChange,
|
|
|
37
38
|
const [savedValues, setSavedValues] = useState(policy);
|
|
38
39
|
const dirty = TIERS.some((tier) => values[tier.key] !== savedValues[tier.key]);
|
|
39
40
|
useEffect(() => onDirtyChange?.(dirty), [dirty]);
|
|
40
|
-
|
|
41
|
+
// Tiers this save would newly set to "Never encrypt", which switches encryption off for everyone's mail of that kind: confirmed first.
|
|
42
|
+
const [confirmingProhibit, setConfirmingProhibit] = useState(null);
|
|
43
|
+
function handleSubmit(e) {
|
|
41
44
|
e.preventDefault();
|
|
45
|
+
const newlyProhibited = TIERS.filter((tier) => values[tier.key] === "prohibited" && savedValues[tier.key] !== "prohibited");
|
|
46
|
+
if (newlyProhibited.length > 0) {
|
|
47
|
+
setConfirmingProhibit(newlyProhibited.map((tier) => tier.label));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
void save();
|
|
51
|
+
}
|
|
52
|
+
async function save() {
|
|
53
|
+
setConfirmingProhibit(null);
|
|
42
54
|
setError(null);
|
|
43
55
|
setSaved(false);
|
|
44
56
|
setSaving(true);
|
|
@@ -49,7 +61,7 @@ export default function EncryptionPolicyForm({ policy, onChange, onDirtyChange,
|
|
|
49
61
|
setSaved(true);
|
|
50
62
|
}
|
|
51
63
|
catch (err) {
|
|
52
|
-
setError(err
|
|
64
|
+
setError(actionErrorMessage(err, "Could not save the encryption policy."));
|
|
53
65
|
}
|
|
54
66
|
finally {
|
|
55
67
|
setSaving(false);
|
|
@@ -58,5 +70,5 @@ export default function EncryptionPolicyForm({ policy, onChange, onDirtyChange,
|
|
|
58
70
|
return (_jsxs("div", { className: "max-w-xl", children: [embedded ? (_jsx("h3", { className: "text-base font-bold uppercase tracking-wide mb-1", children: "End-to-end encryption" })) : (_jsx("h2", { className: "text-lg font-bold uppercase tracking-wide mb-1", children: "End-to-end encryption" })), _jsx("p", { className: "text-sm text-text-muted mb-5", children: "Encrypted messages can only be read by their senders and recipients - not by this server or its administrators. Choose how encryption applies to each kind of mail." }), error && _jsx(Alert, { children: error }), saved && !error && _jsx("div", { className: "mb-4 text-sm text-success font-medium", children: "Saved." }), _jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [TIERS.map((tier) => (_jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: tier.label }), _jsx("select", { "aria-label": tier.label, className: SELECT_CLASS, value: values[tier.key], onChange: (e) => {
|
|
59
71
|
setSaved(false);
|
|
60
72
|
setValues({ ...values, [tier.key]: e.target.value });
|
|
61
|
-
}, children: STATES.map((state) => (_jsx("option", { value: state.value, children: state.label }, state.value))) }), _jsx("span", { className: "text-xs text-text-muted", children: tier.help })] }, tier.key))), _jsx("div", { children: _jsx(Button, { type: "submit", loading: saving, disabled: saving, className: "!w-auto", children: "Save" }) })] })] }));
|
|
73
|
+
}, children: STATES.map((state) => (_jsx("option", { value: state.value, children: state.label }, state.value))) }), _jsx("span", { className: "text-xs text-text-muted", children: tier.help })] }, tier.key))), _jsx("div", { children: _jsx(Button, { type: "submit", loading: saving, disabled: saving, className: "!w-auto", children: "Save" }) })] }), _jsxs(Modal, { open: confirmingProhibit !== null, onClose: () => setConfirmingProhibit(null), title: "Turn off encryption?", children: [_jsxs("p", { className: "text-sm mb-3", children: ["Saving sets ", _jsx("strong", { children: confirmingProhibit?.join(", ") }), " to Never encrypt. Nobody on this server can then encrypt that mail end to end, whatever they have chosen for themselves, until an administrator allows it again."] }), _jsxs("div", { className: "flex gap-3 justify-end", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingProhibit(null), children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger", onClick: () => void save(), children: "Save and turn off encryption" })] })] })] }));
|
|
62
74
|
}
|
|
@@ -4,8 +4,8 @@ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
import { useEffect, useState } from "react";
|
|
7
|
-
import {
|
|
8
|
-
import Alert from "
|
|
7
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
8
|
+
import Alert from "../ActionAlert.js";
|
|
9
9
|
/** Loads a settings object, then renders its form - the loading and error states every settings page shares. */
|
|
10
10
|
export default function LoadedSettingsForm({ load, loadErrorMessage, children }) {
|
|
11
11
|
const [value, setValue] = useState(null);
|
|
@@ -13,7 +13,7 @@ export default function LoadedSettingsForm({ load, loadErrorMessage, children })
|
|
|
13
13
|
useEffect(() => {
|
|
14
14
|
load()
|
|
15
15
|
.then(setValue)
|
|
16
|
-
.catch((err) => setError(err
|
|
16
|
+
.catch((err) => setError(actionErrorMessage(err, loadErrorMessage)));
|
|
17
17
|
}, []);
|
|
18
18
|
if (error) {
|
|
19
19
|
return _jsx(Alert, { children: error });
|
|
@@ -4,17 +4,17 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
import { useEffect, useRef, useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
8
7
|
import { getMailboxPolicy } from "../../../../../lib/admin/mailboxPolicyApi.js";
|
|
9
8
|
import { leftoverConflictOf } from "../../../../../lib/admin/leftoverMailboxApi.js";
|
|
10
9
|
import { createMailbox, listMailboxDomains, resolveMailboxOwner } from "../../../../../lib/mail/mailApi.js";
|
|
11
10
|
import { describeTimeZone, deviceTimeZone, zoneClock } from "../../../../../lib/util/timeZone.js";
|
|
12
11
|
import { useApiClient } from "../../../../../lib/util/apiClientContext.js";
|
|
13
|
-
import Alert from "
|
|
12
|
+
import Alert from "../ActionAlert.js";
|
|
14
13
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
15
14
|
import FormField from "../../../../../lib/components/forms/FormField.js";
|
|
16
15
|
import PrincipalResolver, { describePerson } from "../../sharing/PrincipalResolver.js";
|
|
17
16
|
import EraseLeftoverDataDialog from "../mailboxes/EraseLeftoverDataDialog.js";
|
|
17
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
18
18
|
const INPUT_CLASS = "w-full text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
19
19
|
const SELECT_CLASS = "text-sm py-2.5 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
20
20
|
/**
|
|
@@ -82,6 +82,11 @@ export default function MailboxCreateForm({ onCreated, defaults, submitLabel = "
|
|
|
82
82
|
setError("A primary SMTP address is required.");
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
|
+
// With the domain chosen beside it, a local part with an "@" would make `a@b.com@domain`, which the server refuses without saying why.
|
|
86
|
+
if (constrained && /[@@﹫]/.test(localPart)) {
|
|
87
|
+
setError("The part before the domain can't contain \"@\" - choose the domain from the list instead.");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
85
90
|
if (!displayName.trim()) {
|
|
86
91
|
setError("A display name is required.");
|
|
87
92
|
return;
|
|
@@ -125,7 +130,7 @@ export default function MailboxCreateForm({ onCreated, defaults, submitLabel = "
|
|
|
125
130
|
setLeftover({ address, conflict });
|
|
126
131
|
}
|
|
127
132
|
else {
|
|
128
|
-
setError(err
|
|
133
|
+
setError(actionErrorMessage(err, "Could not create the mailbox."));
|
|
129
134
|
}
|
|
130
135
|
}
|
|
131
136
|
finally {
|
|
@@ -4,10 +4,10 @@ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-run
|
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
import { useEffect, useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "../../../../../lib/util/api.js";
|
|
8
7
|
import { updateMailboxPolicy } from "../../../../../lib/admin/mailboxPolicyApi.js";
|
|
9
|
-
import Alert from "
|
|
8
|
+
import Alert from "../ActionAlert.js";
|
|
10
9
|
import Button from "../../../../../lib/components/buttons/Button.js";
|
|
10
|
+
import { actionErrorMessage } from "../elevation.js";
|
|
11
11
|
const INPUT_CLASS = "w-full text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
12
12
|
const GB = 1000000000;
|
|
13
13
|
/** Unrounded, so a quota that isn't a whole number of hundredths of a GB (say 4 MB) is shown as it is. */
|
|
@@ -80,7 +80,7 @@ export default function MailboxPolicyForm({ policy, onChange, onDirtyChange, emb
|
|
|
80
80
|
setSaved(true);
|
|
81
81
|
}
|
|
82
82
|
catch (err) {
|
|
83
|
-
setError(err
|
|
83
|
+
setError(actionErrorMessage(err, "Could not save the mailbox policy."));
|
|
84
84
|
}
|
|
85
85
|
finally {
|
|
86
86
|
setSaving(false);
|