@rapidmx/web-client 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +373 -88
- package/apps/admin/_layout.tsx +8 -2
- package/apps/admin/index.tsx +7 -1
- package/apps/admin/ingest-queue/index.tsx +2 -1
- package/apps/admin/mailboxes/[uid].tsx +23 -10
- package/apps/admin/quarantine/index.tsx +2 -1
- package/apps/admin/signing-certificates/index.tsx +343 -0
- package/apps/escrow/_layout.tsx +8 -2
- package/apps/shared/appearance/AppearanceForm.tsx +392 -0
- package/apps/shared/appearance/AppearanceHead.tsx +49 -0
- package/apps/shared/appearance/AppearanceProvider.tsx +470 -0
- package/apps/shared/appearance/ColorField.tsx +107 -0
- package/apps/shared/appearance/appearanceCache.ts +106 -0
- package/apps/shared/appearance/appearanceContext.ts +77 -0
- package/apps/shared/appearance/bootScript.ts +35 -0
- package/apps/shared/appearance/color.ts +169 -0
- package/apps/shared/appearance/photo.ts +88 -0
- package/apps/shared/appearance/resolvedTheme.ts +67 -0
- package/apps/shared/appearance/theme.ts +355 -0
- package/apps/shared/auth/accountUrl.ts +9 -0
- package/apps/shared/auth/adminAccess.ts +99 -0
- package/apps/shared/components/admin/layout/AdminShell.tsx +22 -10
- package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +107 -60
- package/apps/shared/components/admin/settings/BrandingForm.tsx +24 -0
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +2 -1
- package/apps/shared/components/admin/settings/PluginsManager.tsx +228 -25
- package/apps/shared/components/admin/setup/SetupWizard.tsx +2 -1
- package/apps/shared/components/calendar/CalendarListSidebar.tsx +4 -5
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +8 -2
- package/apps/shared/components/contacts/ContactForm.tsx +11 -5
- package/apps/shared/components/contacts/ContactsSidebar.tsx +5 -3
- package/apps/shared/components/contacts/ContactsToolbar.tsx +98 -115
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +25 -8
- package/apps/shared/components/escrow/layout/EscrowShell.tsx +13 -6
- package/apps/shared/components/layout/AppShell.tsx +477 -288
- package/apps/shared/components/layout/BrandingChrome.tsx +200 -13
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -389
- package/apps/shared/components/layout/MailboxProvisioning.tsx +168 -164
- package/apps/shared/components/layout/RailIcon.tsx +100 -0
- package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -0
- package/apps/shared/components/layout/ThemeSwitch.tsx +84 -0
- package/apps/shared/components/layout/UserMenu.tsx +372 -185
- package/apps/shared/components/mail/ConversationList.tsx +271 -262
- package/apps/shared/components/mail/ConversationThreadPane.tsx +147 -94
- package/apps/shared/components/mail/LazyReadingPane.tsx +113 -0
- package/apps/shared/components/mail/MailSelectionBar.tsx +227 -222
- package/apps/shared/components/mail/MessageDetailPane.tsx +1537 -1387
- package/apps/shared/components/mail/OutboxBadge.tsx +44 -0
- package/apps/shared/components/mail/OutboxRowStatus.tsx +31 -0
- package/apps/shared/components/mail/compose/ComposeContext.tsx +312 -160
- package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -418
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +1644 -1736
- package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +111 -0
- package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -118
- package/apps/shared/components/mail/compose/composePerf.ts +46 -0
- package/apps/shared/components/mail/compose/encryptionRequirement.ts +86 -0
- package/apps/shared/components/mail/compose/quotedBody.ts +161 -100
- package/apps/shared/components/mail/layout/MailShell.tsx +490 -472
- package/apps/shared/components/mail/reading/EncryptedBody.tsx +65 -0
- package/apps/shared/components/mail/reading/EncryptedPreview.tsx +35 -0
- package/apps/shared/components/mail/reading/MessageBody.tsx +240 -0
- package/apps/shared/components/mail/reading/MessageCard.tsx +174 -0
- package/apps/shared/components/mail/reading/bodyContent.ts +100 -0
- package/apps/shared/components/mail/reading/bodyHtml.ts +298 -0
- package/apps/shared/components/mail/reading/color.ts +245 -0
- package/apps/shared/components/mail/reading/frameControl.ts +142 -0
- package/apps/shared/components/mail/reading/frameDocument.ts +123 -0
- package/apps/shared/components/mail/reading/safeDocument.ts +24 -0
- package/apps/shared/components/mail/reading/themeAdaptation.ts +221 -0
- package/apps/shared/components/mail/reading/themeSurface.ts +90 -0
- package/apps/shared/components/mail/reading/viewOriginal.ts +38 -0
- package/apps/shared/components/mail/unreadStyle.tsx +75 -0
- package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -0
- package/apps/shared/components/settings/layout/SettingsShell.tsx +253 -240
- package/apps/shared/components/sharing/PrincipalPicker.tsx +145 -0
- package/apps/shared/components/tasks/TasksSidebar.tsx +5 -3
- package/apps/shared/components/tasks/layout/TasksShell.tsx +24 -8
- package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -0
- package/apps/shared/keyboard/ShortcutProvider.tsx +62 -0
- package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -0
- package/apps/shared/keyboard/dispatch.ts +124 -0
- package/apps/shared/keyboard/format.ts +89 -0
- package/apps/shared/keyboard/keymap.ts +114 -0
- package/apps/shared/keyboard/match.ts +44 -0
- package/apps/shared/keyboard/parse.ts +136 -0
- package/apps/shared/keyboard/platform.ts +34 -0
- package/apps/shared/keyboard/registry.ts +65 -0
- package/apps/shared/keyboard/targets.ts +79 -0
- package/apps/shared/keyboard/useShortcut.ts +50 -0
- package/apps/shared/keyboard/useShortcutProps.ts +17 -0
- package/apps/shared/mail/folderCounts.ts +308 -0
- package/apps/shared/mail/folderTree.ts +143 -0
- package/apps/shared/mail/listSnapshots.ts +87 -0
- package/apps/shared/mail/messageReadState.ts +85 -0
- package/apps/shared/mail/newMailNotifications.ts +183 -0
- package/apps/shared/mail/outbox/UnlockBridge.tsx +23 -0
- package/apps/shared/mail/outbox/composeBridge.ts +69 -0
- package/apps/shared/mail/outbox/outboxState.ts +88 -0
- package/apps/shared/mail/outbox/pendingSends.ts +146 -0
- package/apps/shared/mail/outbox/sendDecision.ts +157 -0
- package/apps/shared/mail/outbox/sendJob.ts +427 -0
- package/apps/shared/mail/outbox/sendOutcomes.ts +146 -0
- package/apps/shared/mail/outbox/sendState.ts +35 -0
- package/apps/shared/mail/outbox/useOutboxStatus.ts +61 -0
- package/apps/shared/mail/useMailConnection.ts +203 -0
- package/apps/shared/mail/useMailLiveUpdates.ts +277 -224
- package/apps/shared/mail/useMarkMessageRead.ts +47 -0
- package/apps/shared/mail/useNewMailNotifications.ts +178 -0
- package/apps/shared/mail/useUnreadTitle.ts +42 -0
- package/apps/shared/navigation/AppRouter.tsx +300 -0
- package/apps/shared/navigation/appHrefs.ts +23 -0
- package/apps/shared/navigation/frameContext.tsx +35 -0
- package/apps/shared/navigation/idle.ts +45 -0
- package/apps/shared/navigation/routerContext.tsx +83 -0
- package/apps/shared/navigation/routes.ts +72 -0
- package/apps/shared/notifications/NotificationCenter.tsx +215 -0
- package/apps/shared/notifications/NotificationHistoryDialog.tsx +90 -0
- package/apps/shared/notifications/apiErrors.ts +98 -0
- package/apps/shared/notifications/headerOffset.ts +35 -0
- package/apps/shared/notifications/pushStatus.ts +56 -0
- package/apps/shared/notifications/store.ts +549 -0
- package/apps/shared/notifications/systemErrors.ts +63 -0
- package/apps/shared/notifications/useNotifications.ts +41 -0
- package/apps/shared/search/LocalIndexLifecycle.tsx +114 -98
- package/apps/shared/signing/enrollmentStorage.ts +33 -0
- package/apps/shared/signing/enrollmentTracker.ts +385 -0
- package/apps/shared/signing/enrollmentView.ts +251 -0
- package/apps/shared/signing/signingInfo.ts +58 -0
- package/apps/shared/signing/useNow.ts +19 -0
- package/apps/shared/signing/useSigningEnrollmentWatcher.ts +90 -0
- package/apps/shared/styles/app.css +108 -10
- package/apps/www/_layout.tsx +8 -2
- package/apps/www/_routedPage.tsx +24 -0
- package/apps/www/_routes.ts +36 -0
- package/apps/www/calendar/index.tsx +64 -33
- package/apps/www/contacts/[uid].tsx +109 -107
- package/apps/www/contacts/index.tsx +62 -42
- package/apps/www/index.tsx +2291 -1917
- package/apps/www/messages/[uid].tsx +108 -101
- package/apps/www/settings/appearance/index.tsx +21 -0
- package/apps/www/settings/auto-reply/index.tsx +7 -8
- package/apps/www/settings/encryption/index.tsx +1287 -1249
- package/apps/www/settings/filters/[uid].tsx +7 -2
- package/apps/www/settings/filters/index.tsx +102 -99
- package/apps/www/settings/filters/new/index.tsx +140 -133
- package/apps/www/settings/labels/index.tsx +204 -201
- package/apps/www/settings/privacy/index.tsx +7 -9
- package/apps/www/settings/read-receipts/index.tsx +7 -8
- package/apps/www/settings/sharing/index.tsx +256 -274
- package/apps/www/settings/signatures/[uid].tsx +172 -167
- package/apps/www/settings/signatures/index.tsx +88 -85
- package/apps/www/settings/signatures/new/index.tsx +135 -129
- package/apps/www/tasks/index.tsx +29 -18
- package/dist/apps/admin/_layout.d.ts +5 -1
- package/dist/apps/admin/_layout.js +3 -2
- package/dist/apps/admin/index.js +3 -2
- package/dist/apps/admin/ingest-queue/index.js +2 -1
- package/dist/apps/admin/mailboxes/[uid].js +9 -6
- package/dist/apps/admin/quarantine/index.js +2 -1
- package/dist/apps/admin/signing-certificates/index.d.ts +3 -0
- package/dist/apps/admin/signing-certificates/index.js +161 -0
- package/dist/apps/escrow/_layout.d.ts +5 -1
- package/dist/apps/escrow/_layout.js +3 -2
- package/dist/apps/shared/appearance/AppearanceForm.d.ts +7 -0
- package/dist/apps/shared/appearance/AppearanceForm.js +131 -0
- package/dist/apps/shared/appearance/AppearanceHead.d.ts +15 -0
- package/dist/apps/shared/appearance/AppearanceHead.js +31 -0
- package/dist/apps/shared/appearance/AppearanceProvider.d.ts +32 -0
- package/dist/apps/shared/appearance/AppearanceProvider.js +390 -0
- package/dist/apps/shared/appearance/ColorField.d.ts +22 -0
- package/dist/apps/shared/appearance/ColorField.js +40 -0
- package/dist/apps/shared/appearance/appearanceCache.d.ts +45 -0
- package/dist/apps/shared/appearance/appearanceCache.js +80 -0
- package/dist/apps/shared/appearance/appearanceContext.d.ts +45 -0
- package/dist/apps/shared/appearance/appearanceContext.js +29 -0
- package/dist/apps/shared/appearance/bootScript.d.ts +16 -0
- package/dist/apps/shared/appearance/bootScript.js +33 -0
- package/dist/apps/shared/appearance/color.d.ts +65 -0
- package/dist/apps/shared/appearance/color.js +132 -0
- package/dist/apps/shared/appearance/photo.d.ts +17 -0
- package/dist/apps/shared/appearance/photo.js +79 -0
- package/dist/apps/shared/appearance/resolvedTheme.d.ts +21 -0
- package/dist/apps/shared/appearance/resolvedTheme.js +55 -0
- package/dist/apps/shared/appearance/theme.d.ts +111 -0
- package/dist/apps/shared/appearance/theme.js +251 -0
- package/dist/apps/shared/auth/accountUrl.d.ts +2 -0
- package/dist/apps/shared/auth/accountUrl.js +8 -0
- package/dist/apps/shared/auth/adminAccess.d.ts +30 -0
- package/dist/apps/shared/auth/adminAccess.js +89 -0
- package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
- package/dist/apps/shared/components/admin/layout/AdminShell.js +16 -6
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.d.ts +10 -5
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +49 -34
- package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +2 -0
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +7 -1
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +1 -1
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +106 -20
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +2 -1
- package/dist/apps/shared/components/calendar/CalendarListSidebar.js +8 -8
- package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +8 -4
- package/dist/apps/shared/components/contacts/ContactForm.js +1 -1
- package/dist/apps/shared/components/contacts/ContactsSidebar.js +4 -2
- package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +6 -2
- package/dist/apps/shared/components/contacts/ContactsToolbar.js +28 -10
- package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
- package/dist/apps/shared/components/contacts/layout/ContactsShell.js +19 -6
- package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -4
- package/dist/apps/shared/components/layout/AppShell.d.ts +34 -4
- package/dist/apps/shared/components/layout/AppShell.js +122 -17
- package/dist/apps/shared/components/layout/BrandingChrome.d.ts +69 -10
- package/dist/apps/shared/components/layout/BrandingChrome.js +140 -12
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +10 -9
- package/dist/apps/shared/components/layout/MailboxProvisioning.js +3 -1
- package/dist/apps/shared/components/layout/RailIcon.d.ts +21 -0
- package/dist/apps/shared/components/layout/RailIcon.js +85 -0
- package/dist/apps/shared/components/layout/ResponsiveToolbar.d.ts +55 -0
- package/dist/apps/shared/components/layout/ResponsiveToolbar.js +199 -0
- package/dist/apps/shared/components/layout/ThemeSwitch.d.ts +9 -0
- package/dist/apps/shared/components/layout/ThemeSwitch.js +54 -0
- package/dist/apps/shared/components/layout/UserMenu.d.ts +25 -3
- package/dist/apps/shared/components/layout/UserMenu.js +88 -7
- package/dist/apps/shared/components/mail/ConversationList.js +5 -11
- package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +6 -2
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +87 -40
- package/dist/apps/shared/components/mail/LazyReadingPane.d.ts +9 -0
- package/dist/apps/shared/components/mail/LazyReadingPane.js +83 -0
- package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +6 -4
- package/dist/apps/shared/components/mail/MailSelectionBar.js +11 -4
- package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +25 -8
- package/dist/apps/shared/components/mail/MessageDetailPane.js +167 -73
- package/dist/apps/shared/components/mail/OutboxBadge.d.ts +17 -0
- package/dist/apps/shared/components/mail/OutboxBadge.js +18 -0
- package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +10 -0
- package/dist/apps/shared/components/mail/OutboxRowStatus.js +19 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +38 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +97 -6
- package/dist/apps/shared/components/mail/compose/ComposeToolbar.js +4 -3
- package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +3 -2
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +316 -339
- package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.d.ts +19 -0
- package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +33 -0
- package/dist/apps/shared/components/mail/compose/RichTextEditor.d.ts +9 -1
- package/dist/apps/shared/components/mail/compose/RichTextEditor.js +20 -2
- package/dist/apps/shared/components/mail/compose/composePerf.d.ts +16 -0
- package/dist/apps/shared/components/mail/compose/composePerf.js +41 -0
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +49 -0
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +35 -0
- package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +13 -0
- package/dist/apps/shared/components/mail/compose/quotedBody.js +67 -11
- package/dist/apps/shared/components/mail/layout/MailShell.d.ts +14 -4
- package/dist/apps/shared/components/mail/layout/MailShell.js +90 -104
- package/dist/apps/shared/components/mail/reading/EncryptedBody.d.ts +21 -0
- package/dist/apps/shared/components/mail/reading/EncryptedBody.js +22 -0
- package/dist/apps/shared/components/mail/reading/EncryptedPreview.d.ts +18 -0
- package/dist/apps/shared/components/mail/reading/EncryptedPreview.js +22 -0
- package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +32 -0
- package/dist/apps/shared/components/mail/reading/MessageBody.js +136 -0
- package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +62 -0
- package/dist/apps/shared/components/mail/reading/MessageCard.js +58 -0
- package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +38 -0
- package/dist/apps/shared/components/mail/reading/bodyContent.js +84 -0
- package/dist/apps/shared/components/mail/reading/bodyHtml.d.ts +47 -0
- package/dist/apps/shared/components/mail/reading/bodyHtml.js +267 -0
- package/dist/apps/shared/components/mail/reading/color.d.ts +63 -0
- package/dist/apps/shared/components/mail/reading/color.js +201 -0
- package/dist/apps/shared/components/mail/reading/frameControl.d.ts +33 -0
- package/dist/apps/shared/components/mail/reading/frameControl.js +120 -0
- package/dist/apps/shared/components/mail/reading/frameDocument.d.ts +52 -0
- package/dist/apps/shared/components/mail/reading/frameDocument.js +98 -0
- package/dist/apps/shared/components/mail/reading/safeDocument.d.ts +3 -0
- package/dist/apps/shared/components/mail/reading/safeDocument.js +12 -0
- package/dist/apps/shared/components/mail/reading/themeAdaptation.d.ts +56 -0
- package/dist/apps/shared/components/mail/reading/themeAdaptation.js +144 -0
- package/dist/apps/shared/components/mail/reading/themeSurface.d.ts +17 -0
- package/dist/apps/shared/components/mail/reading/themeSurface.js +83 -0
- package/dist/apps/shared/components/mail/reading/viewOriginal.d.ts +9 -0
- package/dist/apps/shared/components/mail/reading/viewOriginal.js +32 -0
- package/dist/apps/shared/components/mail/unreadStyle.d.ts +45 -0
- package/dist/apps/shared/components/mail/unreadStyle.js +61 -0
- package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +32 -0
- package/dist/apps/shared/components/settings/SigningCertificateCard.js +98 -0
- package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +16 -5
- package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +26 -0
- package/dist/apps/shared/components/sharing/PrincipalPicker.js +77 -0
- package/dist/apps/shared/components/tasks/TasksSidebar.js +4 -2
- package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +18 -6
- package/dist/apps/shared/keyboard/GlobalShortcuts.d.ts +14 -0
- package/dist/apps/shared/keyboard/GlobalShortcuts.js +37 -0
- package/dist/apps/shared/keyboard/ShortcutProvider.d.ts +20 -0
- package/dist/apps/shared/keyboard/ShortcutProvider.js +52 -0
- package/dist/apps/shared/keyboard/ShortcutsDialog.d.ts +14 -0
- package/dist/apps/shared/keyboard/ShortcutsDialog.js +42 -0
- package/dist/apps/shared/keyboard/dispatch.d.ts +16 -0
- package/dist/apps/shared/keyboard/dispatch.js +109 -0
- package/dist/apps/shared/keyboard/format.d.ts +12 -0
- package/dist/apps/shared/keyboard/format.js +74 -0
- package/dist/apps/shared/keyboard/keymap.d.ts +294 -0
- package/dist/apps/shared/keyboard/keymap.js +84 -0
- package/dist/apps/shared/keyboard/match.d.ts +20 -0
- package/dist/apps/shared/keyboard/match.js +31 -0
- package/dist/apps/shared/keyboard/parse.d.ts +31 -0
- package/dist/apps/shared/keyboard/parse.js +107 -0
- package/dist/apps/shared/keyboard/platform.d.ts +15 -0
- package/dist/apps/shared/keyboard/platform.js +22 -0
- package/dist/apps/shared/keyboard/registry.d.ts +39 -0
- package/dist/apps/shared/keyboard/registry.js +33 -0
- package/dist/apps/shared/keyboard/targets.d.ts +14 -0
- package/dist/apps/shared/keyboard/targets.js +67 -0
- package/dist/apps/shared/keyboard/useShortcut.d.ts +19 -0
- package/dist/apps/shared/keyboard/useShortcut.js +35 -0
- package/dist/apps/shared/keyboard/useShortcutProps.d.ts +10 -0
- package/dist/apps/shared/keyboard/useShortcutProps.js +15 -0
- package/dist/apps/shared/mail/folderCounts.d.ts +78 -0
- package/dist/apps/shared/mail/folderCounts.js +218 -0
- package/dist/apps/shared/mail/folderTree.d.ts +55 -0
- package/dist/apps/shared/mail/folderTree.js +116 -0
- package/dist/apps/shared/mail/listSnapshots.d.ts +46 -0
- package/dist/apps/shared/mail/listSnapshots.js +43 -0
- package/dist/apps/shared/mail/messageReadState.d.ts +32 -0
- package/dist/apps/shared/mail/messageReadState.js +63 -0
- package/dist/apps/shared/mail/newMailNotifications.d.ts +62 -0
- package/dist/apps/shared/mail/newMailNotifications.js +138 -0
- package/dist/apps/shared/mail/outbox/UnlockBridge.d.ts +5 -0
- package/dist/apps/shared/mail/outbox/UnlockBridge.js +18 -0
- package/dist/apps/shared/mail/outbox/composeBridge.d.ts +34 -0
- package/dist/apps/shared/mail/outbox/composeBridge.js +32 -0
- package/dist/apps/shared/mail/outbox/outboxState.d.ts +41 -0
- package/dist/apps/shared/mail/outbox/outboxState.js +42 -0
- package/dist/apps/shared/mail/outbox/pendingSends.d.ts +39 -0
- package/dist/apps/shared/mail/outbox/pendingSends.js +109 -0
- package/dist/apps/shared/mail/outbox/sendDecision.d.ts +80 -0
- package/dist/apps/shared/mail/outbox/sendDecision.js +94 -0
- package/dist/apps/shared/mail/outbox/sendJob.d.ts +68 -0
- package/dist/apps/shared/mail/outbox/sendJob.js +319 -0
- package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +3 -0
- package/dist/apps/shared/mail/outbox/sendOutcomes.js +141 -0
- package/dist/apps/shared/mail/outbox/sendState.d.ts +25 -0
- package/dist/apps/shared/mail/outbox/sendState.js +8 -0
- package/dist/apps/shared/mail/outbox/useOutboxStatus.d.ts +11 -0
- package/dist/apps/shared/mail/outbox/useOutboxStatus.js +49 -0
- package/dist/apps/shared/mail/useMailConnection.d.ts +56 -0
- package/dist/apps/shared/mail/useMailConnection.js +138 -0
- package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +29 -9
- package/dist/apps/shared/mail/useMailLiveUpdates.js +60 -32
- package/dist/apps/shared/mail/useMarkMessageRead.d.ts +12 -0
- package/dist/apps/shared/mail/useMarkMessageRead.js +44 -0
- package/dist/apps/shared/mail/useNewMailNotifications.d.ts +35 -0
- package/dist/apps/shared/mail/useNewMailNotifications.js +130 -0
- package/dist/apps/shared/mail/useUnreadTitle.d.ts +18 -0
- package/dist/apps/shared/mail/useUnreadTitle.js +31 -0
- package/dist/apps/shared/navigation/AppRouter.d.ts +52 -0
- package/dist/apps/shared/navigation/AppRouter.js +242 -0
- package/dist/apps/shared/navigation/appHrefs.d.ts +14 -0
- package/dist/apps/shared/navigation/appHrefs.js +20 -0
- package/dist/apps/shared/navigation/frameContext.d.ts +19 -0
- package/dist/apps/shared/navigation/frameContext.js +24 -0
- package/dist/apps/shared/navigation/idle.d.ts +14 -0
- package/dist/apps/shared/navigation/idle.js +43 -0
- package/dist/apps/shared/navigation/routerContext.d.ts +37 -0
- package/dist/apps/shared/navigation/routerContext.js +56 -0
- package/dist/apps/shared/navigation/routes.d.ts +32 -0
- package/dist/apps/shared/navigation/routes.js +37 -0
- package/dist/apps/shared/notifications/NotificationCenter.d.ts +19 -0
- package/dist/apps/shared/notifications/NotificationCenter.js +90 -0
- package/dist/apps/shared/notifications/NotificationHistoryDialog.d.ts +10 -0
- package/dist/apps/shared/notifications/NotificationHistoryDialog.js +36 -0
- package/dist/apps/shared/notifications/apiErrors.d.ts +19 -0
- package/dist/apps/shared/notifications/apiErrors.js +86 -0
- package/dist/apps/shared/notifications/headerOffset.d.ts +8 -0
- package/dist/apps/shared/notifications/headerOffset.js +33 -0
- package/dist/apps/shared/notifications/pushStatus.d.ts +9 -0
- package/dist/apps/shared/notifications/pushStatus.js +54 -0
- package/dist/apps/shared/notifications/store.d.ts +136 -0
- package/dist/apps/shared/notifications/store.js +416 -0
- package/dist/apps/shared/notifications/systemErrors.d.ts +9 -0
- package/dist/apps/shared/notifications/systemErrors.js +58 -0
- package/dist/apps/shared/notifications/useNotifications.d.ts +17 -0
- package/dist/apps/shared/notifications/useNotifications.js +18 -0
- package/dist/apps/shared/search/LocalIndexLifecycle.js +17 -3
- package/dist/apps/shared/signing/enrollmentStorage.d.ts +10 -0
- package/dist/apps/shared/signing/enrollmentStorage.js +33 -0
- package/dist/apps/shared/signing/enrollmentTracker.d.ts +73 -0
- package/dist/apps/shared/signing/enrollmentTracker.js +307 -0
- package/dist/apps/shared/signing/enrollmentView.d.ts +57 -0
- package/dist/apps/shared/signing/enrollmentView.js +214 -0
- package/dist/apps/shared/signing/signingInfo.d.ts +11 -0
- package/dist/apps/shared/signing/signingInfo.js +48 -0
- package/dist/apps/shared/signing/useNow.d.ts +2 -0
- package/dist/apps/shared/signing/useNow.js +18 -0
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +16 -0
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +87 -0
- package/dist/apps/shared/styles/app.css +108 -10
- package/dist/apps/www/_layout.d.ts +5 -1
- package/dist/apps/www/_layout.js +3 -2
- package/dist/apps/www/_routedPage.d.ts +12 -0
- package/dist/apps/www/_routedPage.js +19 -0
- package/dist/apps/www/_routes.d.ts +11 -0
- package/dist/apps/www/_routes.js +30 -0
- package/dist/apps/www/calendar/index.d.ts +2 -2
- package/dist/apps/www/calendar/index.js +41 -11
- package/dist/apps/www/contacts/[uid].d.ts +3 -9
- package/dist/apps/www/contacts/[uid].js +11 -9
- package/dist/apps/www/contacts/index.d.ts +2 -2
- package/dist/apps/www/contacts/index.js +30 -24
- package/dist/apps/www/index.d.ts +2 -2
- package/dist/apps/www/index.js +332 -48
- package/dist/apps/www/messages/[uid].d.ts +3 -7
- package/dist/apps/www/messages/[uid].js +10 -5
- package/dist/apps/www/settings/appearance/index.d.ts +4 -0
- package/dist/apps/www/settings/appearance/index.js +13 -0
- package/dist/apps/www/settings/auto-reply/index.d.ts +2 -2
- package/dist/apps/www/settings/auto-reply/index.js +6 -7
- package/dist/apps/www/settings/encryption/index.d.ts +2 -2
- package/dist/apps/www/settings/encryption/index.js +106 -96
- package/dist/apps/www/settings/filters/[uid].d.ts +2 -2
- package/dist/apps/www/settings/filters/[uid].js +6 -2
- package/dist/apps/www/settings/filters/index.d.ts +2 -2
- package/dist/apps/www/settings/filters/index.js +3 -1
- package/dist/apps/www/settings/filters/new/index.d.ts +2 -2
- package/dist/apps/www/settings/filters/new/index.js +9 -3
- package/dist/apps/www/settings/labels/index.d.ts +2 -2
- package/dist/apps/www/settings/labels/index.js +3 -1
- package/dist/apps/www/settings/privacy/index.d.ts +2 -2
- package/dist/apps/www/settings/privacy/index.js +8 -9
- package/dist/apps/www/settings/read-receipts/index.d.ts +2 -2
- package/dist/apps/www/settings/read-receipts/index.js +6 -7
- package/dist/apps/www/settings/sharing/index.d.ts +2 -2
- package/dist/apps/www/settings/sharing/index.js +22 -34
- package/dist/apps/www/settings/signatures/[uid].d.ts +2 -2
- package/dist/apps/www/settings/signatures/[uid].js +6 -2
- package/dist/apps/www/settings/signatures/index.d.ts +2 -2
- package/dist/apps/www/settings/signatures/index.js +3 -1
- package/dist/apps/www/settings/signatures/new/index.d.ts +2 -2
- package/dist/apps/www/settings/signatures/new/index.js +9 -4
- package/dist/apps/www/tasks/index.d.ts +2 -2
- package/dist/apps/www/tasks/index.js +22 -16
- package/package.json +2 -2
- package/apps/shared/components/mail/compose/SendFailureAlert.tsx +0 -48
- package/dist/apps/shared/components/mail/compose/SendFailureAlert.d.ts +0 -14
- package/dist/apps/shared/components/mail/compose/SendFailureAlert.js +0 -11
|
@@ -3,6 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
|
+
import { routedPage } from "../../../_routedPage.js";
|
|
7
|
+
import { useNavigate } from "../../../../shared/navigation/AppRouter.js";
|
|
6
8
|
import { useEffect, useState } from "react";
|
|
7
9
|
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
8
10
|
import { listFolders } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
@@ -13,11 +15,13 @@ import { MAIL_FILTER_CONDITION_FIELDS, buildMailFilterActionTypes } from "../_ma
|
|
|
13
15
|
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
14
16
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
15
17
|
import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
|
|
18
|
+
import { notifyApiError } from "../../../../shared/notifications/apiErrors.js";
|
|
16
19
|
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";
|
|
17
|
-
|
|
20
|
+
function NewMailFilterPage(props) {
|
|
18
21
|
return (_jsx(SettingsShell, { ...props, active: "filters", children: _jsx(NewMailFilterForm, {}) }));
|
|
19
22
|
}
|
|
20
23
|
function NewMailFilterForm() {
|
|
24
|
+
const navigate = useNavigate();
|
|
21
25
|
const { mailboxUid } = useSettingsShell();
|
|
22
26
|
const [folders, setFolders] = useState(null);
|
|
23
27
|
const [folderError, setFolderError] = useState(null);
|
|
@@ -54,10 +58,11 @@ function NewMailFilterForm() {
|
|
|
54
58
|
setSaving(true);
|
|
55
59
|
try {
|
|
56
60
|
const created = await createMailFilterRule({ mailboxUid: mailboxUid, name: name.trim(), ...rule });
|
|
57
|
-
|
|
61
|
+
navigate(`/settings/filters/${encodeURIComponent(created.uid)}?mailboxUid=${encodeURIComponent(mailboxUid)}`);
|
|
58
62
|
}
|
|
59
63
|
catch (err) {
|
|
60
|
-
|
|
64
|
+
// A pop-up: `error` above is only the form's own validation.
|
|
65
|
+
notifyApiError(err, "Couldn't create the mail filter");
|
|
61
66
|
}
|
|
62
67
|
finally {
|
|
63
68
|
setSaving(false);
|
|
@@ -65,3 +70,4 @@ function NewMailFilterForm() {
|
|
|
65
70
|
}
|
|
66
71
|
return (_jsx("div", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: _jsxs("div", { className: "max-w-3xl", children: [_jsx("h1", { className: "text-lg font-bold tracking-tight mb-1", children: "New mail filter" }), _jsx("p", { className: "text-sm text-text-muted mb-5", children: "Applied in order once a message is verdicted safe to deliver, before it reaches your Inbox." }), error && _jsx(Alert, { children: error }), folderError && _jsx(Alert, { children: folderError }), folders === null ? (_jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" })) : (_jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-5", children: [_jsx("div", { className: "bg-surface border border-border rounded-md p-6", children: _jsx(FormField, { label: "Name", htmlFor: "name", children: _jsx("input", { id: "name", type: "text", className: INPUT_CLASS, value: name, onChange: (e) => setName(e.target.value), placeholder: "File newsletters" }) }) }), _jsx(RuleBuilder, { value: rule, onChange: setRule, conditionFields: MAIL_FILTER_CONDITION_FIELDS, actionTypes: buildMailFilterActionTypes(folders), showValidation: showValidation }), _jsxs("div", { className: "flex gap-3", children: [_jsx(Button, { type: "submit", loading: saving, disabled: saving, className: "!w-auto", children: "Create filter" }), _jsx("a", { href: `/settings/filters?mailboxUid=${encodeURIComponent(mailboxUid)}`, children: _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", children: "Cancel" }) })] })] }))] }) }));
|
|
67
72
|
}
|
|
73
|
+
export default routedPage("/settings/filters/new", NewMailFilterPage);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
2
|
export type SettingsLabelsPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
-
|
|
3
|
+
declare const _default: import("../../../shared/navigation/AppRouter.js").RoutedPageComponent<SettingsLabelsPageProps>;
|
|
4
|
+
export default _default;
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
|
+
import { routedPage } from "../../_routedPage.js";
|
|
6
7
|
import { useEffect, useState } from "react";
|
|
7
8
|
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
8
9
|
import { createLabel, deleteLabel, listLabels, updateLabel } from "@rapidmx/react-shared/mail/labelsApi.js";
|
|
@@ -11,7 +12,7 @@ import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
|
11
12
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
12
13
|
import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
|
|
13
14
|
import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
|
|
14
|
-
|
|
15
|
+
function SettingsLabelsPage(props) {
|
|
15
16
|
return (_jsx(SettingsShell, { ...props, active: "labels", children: _jsx(LabelsContent, {}) }));
|
|
16
17
|
}
|
|
17
18
|
const DEFAULT_COLOR = "#6366f1";
|
|
@@ -93,3 +94,4 @@ function LabelsContent() {
|
|
|
93
94
|
setDeleteError(null);
|
|
94
95
|
}, children: "Delete" })] })] }, label.uid))) }))] }), _jsxs(Modal, { open: formOpen, onClose: () => setFormOpen(false), title: editing ? "Edit label" : "New label", children: [formError && _jsx(Alert, { children: formError }), _jsx(FormField, { label: "Name", htmlFor: "label-name", children: _jsx("input", { id: "label-name", type: "text", value: formName, onChange: (e) => setFormName(e.target.value), className: "w-full text-sm px-3 py-1.5 rounded-md border border-border bg-surface" }) }), _jsx(FormField, { label: "Color", htmlFor: "label-color", children: _jsx("input", { id: "label-color", type: "color", value: formColor, onChange: (e) => setFormColor(e.target.value), className: "h-9 w-16 rounded-md border border-border bg-surface" }) }), _jsxs("div", { className: "flex justify-end gap-2 mt-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setFormOpen(false), children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: saving, disabled: saving || !formName.trim(), onClick: handleSave, children: "Save" })] })] }), _jsxs(Modal, { open: !!deleting, onClose: () => setDeleting(null), title: "Delete this label?", children: [deleteError && _jsx(Alert, { children: deleteError }), _jsxs("p", { className: "text-sm text-text-muted mb-4", children: ["This removes \u201C", deleting?.name, "\u201D from every message it's applied to. This can't be undone."] }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setDeleting(null), children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: deleteBusy, disabled: deleteBusy, onClick: handleDelete, children: "Delete label" })] })] })] }));
|
|
95
96
|
}
|
|
97
|
+
export default routedPage("/settings/labels", SettingsLabelsPage);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
2
|
export type SettingsPrivacyPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
-
|
|
3
|
+
declare const _default: import("../../../shared/navigation/AppRouter.js").RoutedPageComponent<SettingsPrivacyPageProps>;
|
|
4
|
+
export default _default;
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
|
+
import { routedPage } from "../../_routedPage.js";
|
|
6
7
|
import { useEffect, useRef, useState } from "react";
|
|
7
8
|
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
8
9
|
import { createExportRequest, exportRequestDownloadUrl, listExportRequests, } from "@rapidmx/react-shared/mail/dataExportApi.js";
|
|
@@ -13,6 +14,7 @@ import SettingsShell, { useSettingsShell } from "../../../shared/components/sett
|
|
|
13
14
|
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
14
15
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
15
16
|
import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
|
|
17
|
+
import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
|
|
16
18
|
const NON_MAIL_FOLDER_TYPES = new Set(["calendar", "contacts", "tasks", "notes"]);
|
|
17
19
|
function importFormatFromFilename(filename) {
|
|
18
20
|
return filename.toLowerCase().endsWith(".pst") ? "pst" : "mbox";
|
|
@@ -31,7 +33,7 @@ function statusBadgeClass(status) {
|
|
|
31
33
|
function statusLabel(status) {
|
|
32
34
|
return status.replace(/_/g, " ");
|
|
33
35
|
}
|
|
34
|
-
|
|
36
|
+
function SettingsPrivacyPage(props) {
|
|
35
37
|
return (_jsx(SettingsShell, { ...props, active: "privacy", children: _jsx(PrivacyContent, { userUid: props.userUid }) }));
|
|
36
38
|
}
|
|
37
39
|
function PrivacyContent({ userUid }) {
|
|
@@ -68,7 +70,6 @@ function ExportSection({ mailboxUid, mailboxLabel }) {
|
|
|
68
70
|
const [loadError, setLoadError] = useState(null);
|
|
69
71
|
const [format, setFormat] = useState("json");
|
|
70
72
|
const [creating, setCreating] = useState(false);
|
|
71
|
-
const [createError, setCreateError] = useState(null);
|
|
72
73
|
// The create form below isn't gated behind `loading`, so a fast create-then-reload can resolve before
|
|
73
74
|
// the initial mount fetch does; without this guard the mount fetch's now-stale response would land
|
|
74
75
|
// last and silently revert the list. Only the most-recently-issued load's response is ever applied.
|
|
@@ -92,20 +93,19 @@ function ExportSection({ mailboxUid, mailboxLabel }) {
|
|
|
92
93
|
}, [mailboxUid]);
|
|
93
94
|
async function handleCreate(e) {
|
|
94
95
|
e.preventDefault();
|
|
95
|
-
setCreateError(null);
|
|
96
96
|
setCreating(true);
|
|
97
97
|
try {
|
|
98
98
|
await createExportRequest({ format });
|
|
99
99
|
await loadRequests();
|
|
100
100
|
}
|
|
101
101
|
catch (err) {
|
|
102
|
-
|
|
102
|
+
notifyApiError(err, "Couldn't start the export");
|
|
103
103
|
}
|
|
104
104
|
finally {
|
|
105
105
|
setCreating(false);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
-
return (_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold mb-2", children: "Export my data" }), _jsxs("p", { className: "text-xs text-text-muted mb-3", children: ["Requests a copy of ", _jsx("strong", { children: mailboxLabel }), "\u2019s messages, contacts, calendar, tasks, and notes. This runs in the background and can take a few minutes \u2014 check back here for a download link once it\u2019s ready."] }), _jsxs("form", { onSubmit: handleCreate, className: "flex items-end gap-3 mb-5", children: [_jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Format" }), _jsxs("select", { "aria-label": "Export format", className: "text-sm border border-border rounded-sm py-1.5 px-2 bg-surface", value: format, onChange: (e) => setFormat(e.target.value), children: [_jsx("option", { value: "json", children: "JSON" }), _jsx("option", { value: "mbox", children: "Mbox" })] })] }), _jsx(Button, { type: "submit", variant: "secondary", className: "!w-auto", loading: creating, disabled: creating, children: "Request export" })] }),
|
|
108
|
+
return (_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold mb-2", children: "Export my data" }), _jsxs("p", { className: "text-xs text-text-muted mb-3", children: ["Requests a copy of ", _jsx("strong", { children: mailboxLabel }), "\u2019s messages, contacts, calendar, tasks, and notes. This runs in the background and can take a few minutes \u2014 check back here for a download link once it\u2019s ready."] }), _jsxs("form", { onSubmit: handleCreate, className: "flex items-end gap-3 mb-5", children: [_jsxs("label", { className: "flex flex-col gap-1.5 text-sm", children: [_jsx("span", { className: "font-semibold", children: "Format" }), _jsxs("select", { "aria-label": "Export format", className: "text-sm border border-border rounded-sm py-1.5 px-2 bg-surface", value: format, onChange: (e) => setFormat(e.target.value), children: [_jsx("option", { value: "json", children: "JSON" }), _jsx("option", { value: "mbox", children: "Mbox" })] })] }), _jsx(Button, { type: "submit", variant: "secondary", className: "!w-auto", loading: creating, disabled: creating, children: "Request export" })] }), loadError && _jsx(Alert, { children: loadError }), loading ? (_jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" })) : requests.length === 0 ? (_jsx("p", { className: "text-sm text-text-muted", children: "No export requests yet." })) : (_jsx("ul", { className: "flex flex-col gap-2", children: requests.map((request) => (_jsxs("li", { className: "flex items-center justify-between gap-3 text-sm py-1.5 px-3 bg-surface-alt rounded-sm", children: [_jsxs("span", { children: [request.format.toUpperCase(), " \u00B7 ", new Date(request.dateCreated).toLocaleString(), request.status === "failed" && request.errorMessage && (_jsxs("span", { className: "text-danger", children: [" \u2014 ", request.errorMessage] }))] }), _jsxs("span", { className: "flex items-center gap-3", children: [_jsx("span", { className: `text-xs font-bold uppercase tracking-wide py-0.5 px-2 rounded-pill ${statusBadgeClass(request.status)}`, children: statusLabel(request.status) }), request.status === "ready" && (_jsx("a", { href: exportRequestDownloadUrl(request.uid), className: "text-primary-dark hover:underline font-medium", children: "Download" }))] })] }, request.uid))) }))] }));
|
|
109
109
|
}
|
|
110
110
|
function ImportSection({ mailboxUid }) {
|
|
111
111
|
const [folders, setFolders] = useState([]);
|
|
@@ -115,7 +115,6 @@ function ImportSection({ mailboxUid }) {
|
|
|
115
115
|
const [loading, setLoading] = useState(true);
|
|
116
116
|
const [loadError, setLoadError] = useState(null);
|
|
117
117
|
const [uploading, setUploading] = useState(false);
|
|
118
|
-
const [uploadError, setUploadError] = useState(null);
|
|
119
118
|
const fileInputRef = useRef(null);
|
|
120
119
|
// Same stale-response guard as ExportSection's loadRequests() - the Upload button is enabled
|
|
121
120
|
// independently of this section's own `loading`, so a fast upload-then-reload can resolve before the
|
|
@@ -155,14 +154,13 @@ function ImportSection({ mailboxUid }) {
|
|
|
155
154
|
if (!file || !targetFolderUid) {
|
|
156
155
|
return;
|
|
157
156
|
}
|
|
158
|
-
setUploadError(null);
|
|
159
157
|
setUploading(true);
|
|
160
158
|
uploadMailboxImport(file, { format: importFormatFromFilename(file.name), targetFolderUid })
|
|
161
159
|
.then(() => loadRequests())
|
|
162
|
-
.catch((err) =>
|
|
160
|
+
.catch((err) => notifyApiError(err, "Couldn't upload the file"))
|
|
163
161
|
.finally(() => setUploading(false));
|
|
164
162
|
}
|
|
165
|
-
return (_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold mb-2", children: "Import mail" }), _jsx("p", { className: "text-xs text-text-muted mb-3", children: "Uploads an Mbox or PST archive and imports every message it contains into the folder you choose below. This runs in the background \u2014 check back here for its progress." }), folders.length > 0 && (_jsxs("label", { className: "flex flex-col gap-1.5 text-sm mb-3 max-w-xs", children: [_jsx("span", { className: "font-semibold", children: "Destination folder" }), _jsx("select", { "aria-label": "Destination folder", className: "text-sm border border-border rounded-sm py-1.5 px-2 bg-surface", value: targetFolderUid, onChange: (e) => setTargetFolderUid(e.target.value), children: folders.map((folder) => (_jsx("option", { value: folder.uid, children: folder.name }, folder.uid))) })] })), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto mb-3", loading: uploading, disabled: uploading || !targetFolderUid, onClick: () => fileInputRef.current?.click(), children: "Upload Mbox or PST file" }), _jsx("input", { ref: fileInputRef, type: "file", accept: ".mbox,.pst", "aria-label": "Upload mail archive", className: "hidden", onChange: handleFileChange }), foldersError && _jsx(Alert, { children: foldersError }),
|
|
163
|
+
return (_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold mb-2", children: "Import mail" }), _jsx("p", { className: "text-xs text-text-muted mb-3", children: "Uploads an Mbox or PST archive and imports every message it contains into the folder you choose below. This runs in the background \u2014 check back here for its progress." }), folders.length > 0 && (_jsxs("label", { className: "flex flex-col gap-1.5 text-sm mb-3 max-w-xs", children: [_jsx("span", { className: "font-semibold", children: "Destination folder" }), _jsx("select", { "aria-label": "Destination folder", className: "text-sm border border-border rounded-sm py-1.5 px-2 bg-surface", value: targetFolderUid, onChange: (e) => setTargetFolderUid(e.target.value), children: folders.map((folder) => (_jsx("option", { value: folder.uid, children: folder.name }, folder.uid))) })] })), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto mb-3", loading: uploading, disabled: uploading || !targetFolderUid, onClick: () => fileInputRef.current?.click(), children: "Upload Mbox or PST file" }), _jsx("input", { ref: fileInputRef, type: "file", accept: ".mbox,.pst", "aria-label": "Upload mail archive", className: "hidden", onChange: handleFileChange }), foldersError && _jsx(Alert, { children: foldersError }), loadError && _jsx(Alert, { children: loadError }), loading ? (_jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" })) : requests.length === 0 ? (_jsx("p", { className: "text-sm text-text-muted", children: "No import requests yet." })) : (_jsx("ul", { className: "flex flex-col gap-2", children: requests.map((request) => (_jsxs("li", { className: "flex items-center justify-between gap-3 text-sm py-1.5 px-3 bg-surface-alt rounded-sm", children: [_jsxs("span", { children: [request.format.toUpperCase(), " \u00B7 ", new Date(request.dateCreated).toLocaleString(), request.status === "completed" && (_jsxs("span", { className: "text-text-muted", children: [" ", "\u2014 ", request.importedCount ?? 0, " imported", request.failedCount ? `, ${request.failedCount} failed` : ""] })), request.status === "failed" && request.errorMessage && (_jsxs("span", { className: "text-danger", children: [" \u2014 ", request.errorMessage] }))] }), _jsx("span", { className: `text-xs font-bold uppercase tracking-wide py-0.5 px-2 rounded-pill ${statusBadgeClass(request.status)}`, children: statusLabel(request.status) })] }, request.uid))) }))] }));
|
|
166
164
|
}
|
|
167
165
|
function ErasureSection({ mailboxLabel }) {
|
|
168
166
|
const [requests, setRequests] = useState([]);
|
|
@@ -200,3 +198,4 @@ function ErasureSection({ mailboxLabel }) {
|
|
|
200
198
|
}
|
|
201
199
|
return (_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold mb-2", children: "Delete my account" }), _jsxs("p", { className: "text-xs text-text-muted mb-3", children: ["Permanently and irreversibly deletes ", _jsx("strong", { children: mailboxLabel }), " and everything in it, once a compliance administrator reviews and approves the request. There is no way to cancel a request or undo the deletion once it runs."] }), loadError && _jsx(Alert, { children: loadError }), loading ? (_jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" })) : (_jsxs(_Fragment, { children: [requests.length > 0 && (_jsx("ul", { className: "flex flex-col gap-2 mb-3", children: requests.map((request) => (_jsxs("li", { className: "flex items-center justify-between gap-3 text-sm py-1.5 px-3 bg-surface-alt rounded-sm", children: [_jsxs("span", { children: [new Date(request.dateCreated).toLocaleString(), request.status === "denied" && request.reason && (_jsxs("span", { className: "text-danger", children: [" \u2014 ", request.reason] }))] }), _jsx("span", { className: `text-xs font-bold uppercase tracking-wide py-0.5 px-2 rounded-pill ${statusBadgeClass(request.status)}`, children: statusLabel(request.status) })] }, request.uid))) })), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto !border-danger !text-danger hover:!border-danger hover:!text-danger", disabled: hasPending, onClick: () => setConfirming(true), children: "Request account erasure" })] })), _jsxs(Modal, { open: confirming, onClose: closeConfirm, title: "Delete my account", children: [_jsxs("p", { className: "text-sm mb-5", children: ["This permanently deletes ", mailboxLabel, " and everything in it \u2014 messages, contacts, calendar, tasks, and notes \u2014 once approved. This cannot be undone and cannot be cancelled once submitted."] }), createError && _jsx(Alert, { children: createError }), _jsxs("div", { className: "flex gap-3 justify-end mt-5", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: creating, onClick: closeConfirm, children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger", loading: creating, disabled: creating, onClick: handleConfirm, children: "Request erasure" })] })] })] }));
|
|
202
200
|
}
|
|
201
|
+
export default routedPage("/settings/privacy", SettingsPrivacyPage);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
2
|
export type SettingsReadReceiptsPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
-
|
|
3
|
+
declare const _default: import("../../../shared/navigation/AppRouter.js").RoutedPageComponent<SettingsReadReceiptsPageProps>;
|
|
4
|
+
export default _default;
|
|
@@ -3,13 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
|
+
import { routedPage } from "../../_routedPage.js";
|
|
6
7
|
import { useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
8
8
|
import { updateMailbox } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
9
9
|
import SettingsShell, { useSettingsShell } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
10
|
-
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
11
10
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
12
|
-
|
|
11
|
+
import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
|
|
12
|
+
function SettingsReadReceiptsPage(props) {
|
|
13
13
|
return (_jsx(SettingsShell, { ...props, active: "read-receipts", children: _jsx(ReadReceiptsContent, {}) }));
|
|
14
14
|
}
|
|
15
15
|
function ReadReceiptsContent() {
|
|
@@ -25,12 +25,10 @@ function ReadReceiptsContent() {
|
|
|
25
25
|
const [autoSendReceiptsExternal, setAutoSendReceiptsExternal] = useState(mailbox.autoSendReceiptsExternal ?? false);
|
|
26
26
|
// See auto-reply/index.tsx's identical note - later saves must carry the version the previous save returned.
|
|
27
27
|
const [version, setVersion] = useState(mailbox.version);
|
|
28
|
-
const [error, setError] = useState(null);
|
|
29
28
|
const [saving, setSaving] = useState(false);
|
|
30
29
|
const [saved, setSaved] = useState(false);
|
|
31
30
|
async function handleSubmit(e) {
|
|
32
31
|
e.preventDefault();
|
|
33
|
-
setError(null);
|
|
34
32
|
setSaved(false);
|
|
35
33
|
setSaving(true);
|
|
36
34
|
try {
|
|
@@ -48,11 +46,12 @@ function ReadReceiptsContent() {
|
|
|
48
46
|
setSaved(true);
|
|
49
47
|
}
|
|
50
48
|
catch (err) {
|
|
51
|
-
|
|
49
|
+
notifyApiError(err, "Couldn't save the read receipt settings");
|
|
52
50
|
}
|
|
53
51
|
finally {
|
|
54
52
|
setSaving(false);
|
|
55
53
|
}
|
|
56
54
|
}
|
|
57
|
-
return (_jsx("div", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: _jsxs("div", { className: "max-w-xl", children: [_jsx("h1", { className: "text-lg font-bold tracking-tight mb-1", children: "Read Receipts" }), _jsx("p", { className: "text-sm text-text-muted mb-4", children: "Controls when this mailbox requests a delivery/read receipt on mail it sends, and how it responds to a receipt requested of it. A per-message \u201CRequest a read receipt\u201D checkbox in Compose always overrides the request-side settings below for that one message." }),
|
|
55
|
+
return (_jsx("div", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: _jsxs("div", { className: "max-w-xl", children: [_jsx("h1", { className: "text-lg font-bold tracking-tight mb-1", children: "Read Receipts" }), _jsx("p", { className: "text-sm text-text-muted mb-4", children: "Controls when this mailbox requests a delivery/read receipt on mail it sends, and how it responds to a receipt requested of it. A per-message \u201CRequest a read receipt\u201D checkbox in Compose always overrides the request-side settings below for that one message." }), saved && _jsx("div", { className: "mb-4 text-sm text-success font-medium", children: "Saved." }), _jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-5", children: [_jsxs("fieldset", { className: "flex flex-col gap-2", children: [_jsx("legend", { className: "text-sm font-semibold mb-1", children: "Request receipts when sending" }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: alwaysRequestReceiptInternal, onChange: (e) => setAlwaysRequestReceiptInternal(e.target.checked) }), "From internal recipients (mail to this organization)"] }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: alwaysRequestReceiptFederated, onChange: (e) => setAlwaysRequestReceiptFederated(e.target.checked) }), "From federated recipients (partner organizations)"] }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: alwaysRequestReceiptExternal, onChange: (e) => setAlwaysRequestReceiptExternal(e.target.checked) }), "From external recipients"] })] }), _jsxs("fieldset", { className: "flex flex-col gap-2", children: [_jsx("legend", { className: "text-sm font-semibold mb-1", children: "Respond automatically to requests" }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: autoSendReceiptsInternal, onChange: (e) => setAutoSendReceiptsInternal(e.target.checked) }), "From internal senders"] }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: autoSendReceiptsFederated, onChange: (e) => setAutoSendReceiptsFederated(e.target.checked) }), "From federated senders (partner organizations)"] }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: autoSendReceiptsExternal, onChange: (e) => setAutoSendReceiptsExternal(e.target.checked) }), "From external senders"] }), _jsx("p", { className: "text-xs text-text-muted", children: "Unchecked means a request from that sender waits in your Inbox for you to send or decline it explicitly." })] }), _jsx("div", { children: _jsx(Button, { type: "submit", loading: saving, disabled: saving, className: "!w-auto", children: "Save" }) })] })] }) }));
|
|
58
56
|
}
|
|
57
|
+
export default routedPage("/settings/read-receipts", SettingsReadReceiptsPage);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
2
|
export type SettingsSharingPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
-
|
|
3
|
+
declare const _default: import("../../../shared/navigation/AppRouter.js").RoutedPageComponent<SettingsSharingPageProps>;
|
|
4
|
+
export default _default;
|
|
@@ -3,16 +3,19 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
|
+
import { routedPage } from "../../_routedPage.js";
|
|
6
7
|
import { useEffect, useState } from "react";
|
|
7
8
|
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
8
|
-
import { listMailboxAccess,
|
|
9
|
+
import { listMailboxAccess, removeMailboxAccess, setMailboxAccess, } from "@rapidmx/react-shared/mail/mailboxAccessApi.js";
|
|
9
10
|
import SettingsShell, { useSettingsShell } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
11
|
+
import PrincipalPicker from "../../../shared/components/sharing/PrincipalPicker.js";
|
|
10
12
|
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
11
13
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
12
14
|
import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
|
|
13
15
|
import { SkeletonList } from "@rapidmx/react-shared/components/feedback/Skeleton.js";
|
|
16
|
+
import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
|
|
14
17
|
const ROLE_LABELS = { viewer: "Can view", manager: "Can manage" };
|
|
15
|
-
|
|
18
|
+
function SettingsSharingPage(props) {
|
|
16
19
|
return (_jsx(SettingsShell, { ...props, active: "sharing", children: _jsx(SharingContent, {}) }));
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
@@ -29,10 +32,8 @@ function SharingContent() {
|
|
|
29
32
|
const [members, setMembers] = useState([]);
|
|
30
33
|
const [status, setStatus] = useState("loading");
|
|
31
34
|
const [error, setError] = useState(null);
|
|
32
|
-
|
|
33
|
-
const [
|
|
34
|
-
const [adding, setAdding] = useState(false);
|
|
35
|
-
const [addError, setAddError] = useState(null);
|
|
35
|
+
// An entry that is not a user uid, being replaced with the user it was meant for.
|
|
36
|
+
const [replacing, setReplacing] = useState(null);
|
|
36
37
|
const [pendingRoleChange, setPendingRoleChange] = useState(null);
|
|
37
38
|
const [removeTarget, setRemoveTarget] = useState(null);
|
|
38
39
|
const [removing, setRemoving] = useState(false);
|
|
@@ -60,41 +61,28 @@ function SharingContent() {
|
|
|
60
61
|
});
|
|
61
62
|
}
|
|
62
63
|
useEffect(reload, [mailboxUid]);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!found) {
|
|
74
|
-
setAddError("No one found with that email on this platform.");
|
|
75
|
-
return;
|
|
64
|
+
// Once the person is granted, the entry they replace (if any) is removed: the string it named never matched anyone.
|
|
65
|
+
async function handleGranted() {
|
|
66
|
+
const stale = replacing;
|
|
67
|
+
setReplacing(null);
|
|
68
|
+
if (stale) {
|
|
69
|
+
try {
|
|
70
|
+
await removeMailboxAccess(mailboxUid, stale.userOrRoleId);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
notifyApiError(err, "Couldn't remove the old entry");
|
|
76
74
|
}
|
|
77
|
-
await setMailboxAccess(mailboxUid, found.userUid, newRole);
|
|
78
|
-
setEmailInput("");
|
|
79
|
-
setNewRole("viewer");
|
|
80
|
-
reload();
|
|
81
|
-
}
|
|
82
|
-
catch (err) {
|
|
83
|
-
setAddError(err instanceof ApiRequestError ? err.message : "Could not grant access.");
|
|
84
|
-
}
|
|
85
|
-
finally {
|
|
86
|
-
setAdding(false);
|
|
87
75
|
}
|
|
76
|
+
reload();
|
|
88
77
|
}
|
|
89
78
|
async function handleRoleChange(userOrRoleId, role) {
|
|
90
79
|
setPendingRoleChange(userOrRoleId);
|
|
91
|
-
setError(null);
|
|
92
80
|
try {
|
|
93
81
|
await setMailboxAccess(mailboxUid, userOrRoleId, role);
|
|
94
82
|
reload();
|
|
95
83
|
}
|
|
96
84
|
catch (err) {
|
|
97
|
-
|
|
85
|
+
notifyApiError(err, "Couldn't change this member's role");
|
|
98
86
|
}
|
|
99
87
|
finally {
|
|
100
88
|
setPendingRoleChange(null);
|
|
@@ -107,18 +95,18 @@ function SharingContent() {
|
|
|
107
95
|
// Only reachable from the confirm modal's own "Remove" button, and `Modal` renders nothing at all
|
|
108
96
|
// unless `removeTarget` is set, so `removeTarget` is always non-null here.
|
|
109
97
|
setRemoving(true);
|
|
110
|
-
setError(null);
|
|
111
98
|
try {
|
|
112
99
|
await removeMailboxAccess(mailboxUid, removeTarget.userOrRoleId);
|
|
113
100
|
setRemoveTarget(null);
|
|
114
101
|
reload();
|
|
115
102
|
}
|
|
116
103
|
catch (err) {
|
|
117
|
-
|
|
104
|
+
notifyApiError(err, "Couldn't remove this member");
|
|
118
105
|
}
|
|
119
106
|
finally {
|
|
120
107
|
setRemoving(false);
|
|
121
108
|
}
|
|
122
109
|
}
|
|
123
|
-
return (_jsx("div", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: _jsxs("div", { className: "max-w-xl flex flex-col gap-6", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-lg font-bold tracking-tight mb-1", children: "Sharing" }), _jsxs("p", { className: "text-sm text-text-muted", children: ["Manage who else can access ", mailbox?.displayName ?? "this mailbox", "\u2019s mail, calendar, contacts, and tasks. Granting access here covers all of it at once - there\u2019s nothing else to configure per data type."] })] }), status === "loading" && _jsx(SkeletonList, { count: 3 }), status === "forbidden" && (_jsx(Alert, { children: "You don\u2019t have permission to manage sharing for this mailbox." })), status === "error" && error && _jsx(Alert, { children: error }), status === "ready" && (_jsxs(_Fragment, { children: [
|
|
110
|
+
return (_jsx("div", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: _jsxs("div", { className: "max-w-xl flex flex-col gap-6", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-lg font-bold tracking-tight mb-1", children: "Sharing" }), _jsxs("p", { className: "text-sm text-text-muted", children: ["Manage who else can access ", mailbox?.displayName ?? "this mailbox", "\u2019s mail, calendar, contacts, and tasks. Granting access here covers all of it at once - there\u2019s nothing else to configure per data type."] })] }), status === "loading" && _jsx(SkeletonList, { count: 3 }), status === "forbidden" && (_jsx(Alert, { children: "You don\u2019t have permission to manage sharing for this mailbox." })), status === "error" && error && _jsx(Alert, { children: error }), status === "ready" && (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold mb-2", children: "People with access" }), members.length === 0 ? (_jsx("p", { className: "text-sm text-text-muted", children: "No one else has access to this mailbox yet." })) : (_jsx("ul", { className: "flex flex-col gap-2", children: members.map((member) => (_jsxs("li", { className: "flex items-center justify-between gap-3 text-sm py-1.5 px-3 bg-surface-alt rounded-sm", children: [_jsxs("span", { className: "truncate", children: [member.userOrRoleId, member.noEffect && (_jsx("span", { className: "block text-xs text-danger", children: "Not a user - this entry has no effect. Replace it with the person it was meant for, or remove it." }))] }), _jsxs("span", { className: "flex items-center gap-2 shrink-0", children: [member.noEffect && (_jsx(Button, { type: "button", variant: "text", className: "!w-auto", onClick: () => setReplacing(member), children: "Replace with a user" })), _jsxs("select", { "aria-label": `Role for ${member.userOrRoleId}`, className: "text-sm border border-border rounded-sm py-1 px-2 bg-surface", value: member.role, disabled: pendingRoleChange === member.userOrRoleId, onChange: (e) => handleRoleChange(member.userOrRoleId, e.target.value), children: [member.role === "custom" && (_jsx("option", { value: "custom", disabled: true, children: "Custom access" })), Object.keys(ROLE_LABELS).map((role) => (_jsx("option", { value: role, children: ROLE_LABELS[role] }, role)))] }), _jsx(Button, { type: "button", variant: "text", className: "!w-auto text-danger", onClick: () => setRemoveTarget(member), children: "Remove" })] })] }, member.userOrRoleId))) }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h2", { className: "text-sm font-semibold", children: replacing ? `Replace ${replacing.userOrRoleId}` : "Add someone" }), _jsx(PrincipalPicker, { mailboxUid: mailboxUid, roleLabels: ROLE_LABELS, initialPrincipal: replacing?.userOrRoleId, defaultRole: replacing?.role === "manager" ? "manager" : "viewer", placeholder: "Email address or username", onGranted: handleGranted, onCancel: replacing ? () => setReplacing(null) : undefined }, replacing?.userOrRoleId ?? "new")] })] })), _jsxs(Modal, { open: !!removeTarget, onClose: closeRemoveModal, title: "Remove access", children: [_jsxs("p", { className: "text-sm mb-5", children: [removeTarget?.userOrRoleId, " will no longer be able to access ", mailbox?.displayName ?? "this mailbox", "."] }), _jsxs("div", { className: "flex gap-3 justify-end", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: removing, onClick: closeRemoveModal, children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger", loading: removing, disabled: removing, onClick: handleRemove, children: "Remove" })] })] })] }) }));
|
|
124
111
|
}
|
|
112
|
+
export default routedPage("/settings/sharing", SettingsSharingPage);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
2
|
export type SignatureDetailPageProps = Omit<SettingsShellProps, "active"> & {
|
|
4
3
|
params: {
|
|
5
4
|
uid: string;
|
|
6
5
|
};
|
|
7
6
|
};
|
|
8
|
-
|
|
7
|
+
declare const _default: import("../../../shared/navigation/AppRouter.js").RoutedPageComponent<SignatureDetailPageProps>;
|
|
8
|
+
export default _default;
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
|
+
import { routedPage } from "../../_routedPage.js";
|
|
6
7
|
import { useEffect, useState } from "react";
|
|
7
8
|
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
8
9
|
import { getMailSignature, listMailSignatures, updateMailSignature } from "@rapidmx/react-shared/mail/mailSignaturesApi.js";
|
|
@@ -12,13 +13,14 @@ import RichTextEditor from "../../../shared/components/mail/compose/RichTextEdit
|
|
|
12
13
|
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
13
14
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
14
15
|
import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
|
|
16
|
+
import { notifyApiError } from "../../../shared/notifications/apiErrors.js";
|
|
15
17
|
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";
|
|
16
18
|
/** Signatures aren't backed by a draft/attachment record the way Compose's own inline images are — see
|
|
17
19
|
* `settings/signatures/new/index.tsx`'s identical stub for the full reasoning. */
|
|
18
20
|
async function handleUploadImage() {
|
|
19
21
|
return null;
|
|
20
22
|
}
|
|
21
|
-
|
|
23
|
+
function SignatureDetailPage(props) {
|
|
22
24
|
return (_jsx(SettingsShell, { ...props, active: "signatures", children: _jsx(SignatureDetailContent, { uid: props.params.uid }) }));
|
|
23
25
|
}
|
|
24
26
|
function SignatureDetailContent({ uid }) {
|
|
@@ -77,7 +79,8 @@ function SignatureDetailContent({ uid }) {
|
|
|
77
79
|
setSaved(true);
|
|
78
80
|
}
|
|
79
81
|
catch (err) {
|
|
80
|
-
|
|
82
|
+
// A pop-up: `error` is only this form's own validation (and the load failure above).
|
|
83
|
+
notifyApiError(err, "Couldn't save the signature");
|
|
81
84
|
}
|
|
82
85
|
finally {
|
|
83
86
|
setSaving(false);
|
|
@@ -94,3 +97,4 @@ function SignatureDetailContent({ uid }) {
|
|
|
94
97
|
}
|
|
95
98
|
return (_jsx("div", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: _jsxs("div", { className: "max-w-3xl", children: [_jsx("a", { href: `/settings/signatures?mailboxUid=${encodeURIComponent(mailboxUid)}`, className: "text-sm text-primary-dark hover:underline", children: "\u2190 All signatures" }), _jsx("h1", { className: "text-lg font-bold tracking-tight mt-1 mb-5", children: original.name }), 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-5", children: [_jsxs("div", { className: "bg-surface border border-border rounded-md p-6 flex flex-col gap-4", children: [_jsx(FormField, { label: "Name", htmlFor: "name", children: _jsx("input", { id: "name", type: "text", className: INPUT_CLASS, value: name, onChange: (e) => setName(e.target.value) }) }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: isDefaultForNewMessages, onChange: (e) => setIsDefaultForNewMessages(e.target.checked) }), "Use for new messages"] }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: isDefaultForReplyForward, onChange: (e) => setIsDefaultForReplyForward(e.target.checked) }), "Use for replies and forwards"] })] }), _jsx("div", { className: "h-64", children: _jsx(RichTextEditor, { value: contentHtml, onChange: setContentHtml, fill: true, onUploadImage: handleUploadImage }) }), _jsx("div", { children: _jsx(Button, { type: "submit", loading: saving, disabled: saving, className: "!w-auto", children: "Save changes" }) })] })] }) }));
|
|
96
99
|
}
|
|
100
|
+
export default routedPage("/settings/signatures/:uid", SignatureDetailPage);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
2
|
export type SettingsSignaturesPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
-
|
|
3
|
+
declare const _default: import("../../../shared/navigation/AppRouter.js").RoutedPageComponent<SettingsSignaturesPageProps>;
|
|
4
|
+
export default _default;
|
|
@@ -3,13 +3,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
|
+
import { routedPage } from "../../_routedPage.js";
|
|
6
7
|
import { useEffect, useState } from "react";
|
|
7
8
|
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
8
9
|
import { listMailSignatures } from "@rapidmx/react-shared/mail/mailSignaturesApi.js";
|
|
9
10
|
import SettingsShell, { useSettingsShell } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
10
11
|
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
11
12
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
12
|
-
|
|
13
|
+
function SettingsSignaturesPage(props) {
|
|
13
14
|
return (_jsx(SettingsShell, { ...props, active: "signatures", children: _jsx(SignaturesContent, {}) }));
|
|
14
15
|
}
|
|
15
16
|
function SignaturesContent() {
|
|
@@ -32,3 +33,4 @@ function SignaturesContent() {
|
|
|
32
33
|
.filter(Boolean)
|
|
33
34
|
.join(" · ") }))] }), _jsx("a", { href: `/settings/signatures/${encodeURIComponent(sig.uid)}?mailboxUid=${encodeURIComponent(mailboxUid)}`, className: "text-primary-dark hover:underline font-medium text-sm", children: "Edit" })] }, sig.uid))) }))] }) }));
|
|
34
35
|
}
|
|
36
|
+
export default routedPage("/settings/signatures", SettingsSignaturesPage);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { SettingsShellProps } from "../../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
2
|
export type NewSignaturePageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
-
|
|
3
|
+
declare const _default: import("../../../../shared/navigation/AppRouter.js").RoutedPageComponent<NewSignaturePageProps>;
|
|
4
|
+
export default _default;
|
|
@@ -3,8 +3,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
4
4
|
// SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
|
+
import { routedPage } from "../../../_routedPage.js";
|
|
7
|
+
import { useNavigate } from "../../../../shared/navigation/AppRouter.js";
|
|
6
8
|
import { useState } from "react";
|
|
7
|
-
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
8
9
|
import { createMailSignature, listMailSignatures } from "@rapidmx/react-shared/mail/mailSignaturesApi.js";
|
|
9
10
|
import { clearPreviousDefaults } from "../signatureDefaults.js";
|
|
10
11
|
import SettingsShell, { useSettingsShell } from "../../../../shared/components/settings/layout/SettingsShell.js";
|
|
@@ -12,8 +13,9 @@ import RichTextEditor from "../../../../shared/components/mail/compose/RichTextE
|
|
|
12
13
|
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
13
14
|
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
14
15
|
import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
|
|
16
|
+
import { notifyApiError } from "../../../../shared/notifications/apiErrors.js";
|
|
15
17
|
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";
|
|
16
|
-
|
|
18
|
+
function NewSignaturePage(props) {
|
|
17
19
|
return (_jsx(SettingsShell, { ...props, active: "signatures", children: _jsx(NewSignatureForm, {}) }));
|
|
18
20
|
}
|
|
19
21
|
/** Signatures aren't backed by a draft/attachment record the way Compose's own inline images are (see
|
|
@@ -23,6 +25,7 @@ async function handleUploadImage() {
|
|
|
23
25
|
return null;
|
|
24
26
|
}
|
|
25
27
|
function NewSignatureForm() {
|
|
28
|
+
const navigate = useNavigate();
|
|
26
29
|
const { mailboxUid } = useSettingsShell();
|
|
27
30
|
const [name, setName] = useState("");
|
|
28
31
|
const [contentHtml, setContentHtml] = useState("");
|
|
@@ -50,10 +53,11 @@ function NewSignatureForm() {
|
|
|
50
53
|
isDefaultForNewMessages,
|
|
51
54
|
isDefaultForReplyForward,
|
|
52
55
|
});
|
|
53
|
-
|
|
56
|
+
navigate(`/settings/signatures/${encodeURIComponent(created.uid)}?mailboxUid=${encodeURIComponent(mailboxUid)}`);
|
|
54
57
|
}
|
|
55
58
|
catch (err) {
|
|
56
|
-
|
|
59
|
+
// A pop-up: `error` above is only the form's own validation.
|
|
60
|
+
notifyApiError(err, "Couldn't create the signature");
|
|
57
61
|
}
|
|
58
62
|
finally {
|
|
59
63
|
setSaving(false);
|
|
@@ -61,3 +65,4 @@ function NewSignatureForm() {
|
|
|
61
65
|
}
|
|
62
66
|
return (_jsx("div", { className: "flex-1 min-w-0 overflow-y-auto p-6", children: _jsxs("div", { className: "max-w-3xl", children: [_jsx("h1", { className: "text-lg font-bold tracking-tight mb-5", children: "New signature" }), error && _jsx(Alert, { children: error }), _jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-5", children: [_jsxs("div", { className: "bg-surface border border-border rounded-md p-6 flex flex-col gap-4", children: [_jsx(FormField, { label: "Name", htmlFor: "name", children: _jsx("input", { id: "name", type: "text", className: INPUT_CLASS, value: name, onChange: (e) => setName(e.target.value), placeholder: "Work signature" }) }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: isDefaultForNewMessages, onChange: (e) => setIsDefaultForNewMessages(e.target.checked) }), "Use for new messages"] }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: isDefaultForReplyForward, onChange: (e) => setIsDefaultForReplyForward(e.target.checked) }), "Use for replies and forwards"] })] }), _jsx("div", { className: "h-64", children: _jsx(RichTextEditor, { value: contentHtml, onChange: setContentHtml, fill: true, onUploadImage: handleUploadImage }) }), _jsxs("div", { className: "flex gap-3", children: [_jsx(Button, { type: "submit", loading: saving, disabled: saving, className: "!w-auto", children: "Create signature" }), _jsx("a", { href: `/settings/signatures?mailboxUid=${encodeURIComponent(mailboxUid)}`, children: _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", children: "Cancel" }) })] })] })] }) }));
|
|
63
67
|
}
|
|
68
|
+
export default routedPage("/settings/signatures/new", NewSignaturePage);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { TasksShellProps } from "../../shared/components/tasks/layout/TasksShell.js";
|
|
3
|
-
|
|
2
|
+
declare const _default: import("../../shared/navigation/AppRouter.js").RoutedPageComponent<TasksShellProps>;
|
|
3
|
+
export default _default;
|