@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
|
@@ -1,129 +1,135 @@
|
|
|
1
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
-
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
-
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const [
|
|
41
|
-
const [
|
|
42
|
-
const [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
</div>
|
|
114
|
-
|
|
115
|
-
<div className="
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
import { routedPage } from "../../../_routedPage.js";
|
|
6
|
+
import { useNavigate } from "../../../../shared/navigation/AppRouter.js";
|
|
7
|
+
import React, { FormEvent, useState } from "react";
|
|
8
|
+
import { createMailSignature, listMailSignatures } from "@rapidmx/react-shared/mail/mailSignaturesApi.js";
|
|
9
|
+
import { clearPreviousDefaults } from "../signatureDefaults.js";
|
|
10
|
+
import SettingsShell, { SettingsShellProps, useSettingsShell } from "../../../../shared/components/settings/layout/SettingsShell.js";
|
|
11
|
+
import RichTextEditor from "../../../../shared/components/mail/compose/RichTextEditor.js";
|
|
12
|
+
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
13
|
+
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
14
|
+
import FormField from "@rapidmx/react-shared/components/forms/FormField.js";
|
|
15
|
+
import { notifyApiError } from "../../../../shared/notifications/apiErrors.js";
|
|
16
|
+
|
|
17
|
+
const INPUT_CLASS =
|
|
18
|
+
"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
|
+
|
|
20
|
+
export type NewSignaturePageProps = Omit<SettingsShellProps, "active">;
|
|
21
|
+
|
|
22
|
+
function NewSignaturePage(props: NewSignaturePageProps) {
|
|
23
|
+
return (
|
|
24
|
+
<SettingsShell {...props} active="signatures">
|
|
25
|
+
<NewSignatureForm />
|
|
26
|
+
</SettingsShell>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Signatures aren't backed by a draft/attachment record the way Compose's own inline images are (see
|
|
31
|
+
* `ComposeWindow.handleUploadImage()`) — inline images in a signature are out of scope for this phase
|
|
32
|
+
* (`.claude/NOTES.md`'s Phase 12 entry), so this is a real, deliberate no-op, not a stub left unfinished. */
|
|
33
|
+
async function handleUploadImage(): Promise<string | null> {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function NewSignatureForm() {
|
|
38
|
+
const navigate = useNavigate();
|
|
39
|
+
const { mailboxUid } = useSettingsShell();
|
|
40
|
+
const [name, setName] = useState("");
|
|
41
|
+
const [contentHtml, setContentHtml] = useState("");
|
|
42
|
+
const [isDefaultForNewMessages, setIsDefaultForNewMessages] = useState(false);
|
|
43
|
+
const [isDefaultForReplyForward, setIsDefaultForReplyForward] = useState(false);
|
|
44
|
+
const [error, setError] = useState<string | null>(null);
|
|
45
|
+
const [saving, setSaving] = useState(false);
|
|
46
|
+
|
|
47
|
+
async function handleSubmit(e: FormEvent) {
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
setError(null);
|
|
50
|
+
|
|
51
|
+
if (!name.trim()) {
|
|
52
|
+
setError("A name is required.");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setSaving(true);
|
|
57
|
+
try {
|
|
58
|
+
if (isDefaultForNewMessages || isDefaultForReplyForward) {
|
|
59
|
+
const existing = await listMailSignatures(mailboxUid!, { limit: 100 });
|
|
60
|
+
await clearPreviousDefaults(existing, undefined, isDefaultForNewMessages, isDefaultForReplyForward);
|
|
61
|
+
}
|
|
62
|
+
const created = await createMailSignature({
|
|
63
|
+
mailboxUid: mailboxUid!,
|
|
64
|
+
name: name.trim(),
|
|
65
|
+
contentHtml,
|
|
66
|
+
isDefaultForNewMessages,
|
|
67
|
+
isDefaultForReplyForward,
|
|
68
|
+
});
|
|
69
|
+
navigate(`/settings/signatures/${encodeURIComponent(created.uid)}?mailboxUid=${encodeURIComponent(mailboxUid!)}`);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
// A pop-up: `error` above is only the form's own validation.
|
|
72
|
+
notifyApiError(err, "Couldn't create the signature");
|
|
73
|
+
} finally {
|
|
74
|
+
setSaving(false);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div className="flex-1 min-w-0 overflow-y-auto p-6">
|
|
80
|
+
<div className="max-w-3xl">
|
|
81
|
+
<h1 className="text-lg font-bold tracking-tight mb-5">New signature</h1>
|
|
82
|
+
|
|
83
|
+
{error && <Alert>{error}</Alert>}
|
|
84
|
+
|
|
85
|
+
<form onSubmit={handleSubmit} className="flex flex-col gap-5">
|
|
86
|
+
<div className="bg-surface border border-border rounded-md p-6 flex flex-col gap-4">
|
|
87
|
+
<FormField label="Name" htmlFor="name">
|
|
88
|
+
<input
|
|
89
|
+
id="name"
|
|
90
|
+
type="text"
|
|
91
|
+
className={INPUT_CLASS}
|
|
92
|
+
value={name}
|
|
93
|
+
onChange={(e) => setName(e.target.value)}
|
|
94
|
+
placeholder="Work signature"
|
|
95
|
+
/>
|
|
96
|
+
</FormField>
|
|
97
|
+
<label className="flex items-center gap-2 text-sm">
|
|
98
|
+
<input
|
|
99
|
+
type="checkbox"
|
|
100
|
+
checked={isDefaultForNewMessages}
|
|
101
|
+
onChange={(e) => setIsDefaultForNewMessages(e.target.checked)}
|
|
102
|
+
/>
|
|
103
|
+
Use for new messages
|
|
104
|
+
</label>
|
|
105
|
+
<label className="flex items-center gap-2 text-sm">
|
|
106
|
+
<input
|
|
107
|
+
type="checkbox"
|
|
108
|
+
checked={isDefaultForReplyForward}
|
|
109
|
+
onChange={(e) => setIsDefaultForReplyForward(e.target.checked)}
|
|
110
|
+
/>
|
|
111
|
+
Use for replies and forwards
|
|
112
|
+
</label>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div className="h-64">
|
|
116
|
+
<RichTextEditor value={contentHtml} onChange={setContentHtml} fill onUploadImage={handleUploadImage} />
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div className="flex gap-3">
|
|
120
|
+
<Button type="submit" loading={saving} disabled={saving} className="!w-auto">
|
|
121
|
+
Create signature
|
|
122
|
+
</Button>
|
|
123
|
+
<a href={`/settings/signatures?mailboxUid=${encodeURIComponent(mailboxUid!)}`}>
|
|
124
|
+
<Button type="button" variant="secondary" className="!w-auto">
|
|
125
|
+
Cancel
|
|
126
|
+
</Button>
|
|
127
|
+
</a>
|
|
128
|
+
</div>
|
|
129
|
+
</form>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export default routedPage("/settings/signatures/new", NewSignaturePage);
|
package/apps/www/tasks/index.tsx
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
-
import
|
|
5
|
+
import { routedPage } from "../_routedPage.js";
|
|
6
|
+
import React, { FormEvent, useEffect, useMemo, useRef, useState } from "react";
|
|
6
7
|
import { endOfWeek, isAfter, isBefore, isToday, parseISO, startOfDay } from "date-fns";
|
|
7
8
|
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
8
9
|
import {
|
|
@@ -26,11 +27,15 @@ import Modal from "@rapidmx/react-shared/components/overlays/Modal.js";
|
|
|
26
27
|
import { findWellKnownFolderUid } from "../../shared/mail/findWellKnownFolderUid.js";
|
|
27
28
|
import { useWritableMailboxes } from "../../shared/components/mail/writableMailboxes.js";
|
|
28
29
|
import { LIST_PAGE_SIZE, MAX_LIST_PAGES, listAllPages } from "../../shared/mail/listAllPages.js";
|
|
30
|
+
import { SHORTCUTS } from "../../shared/keyboard/keymap.js";
|
|
31
|
+
import { useShortcut } from "../../shared/keyboard/useShortcut.js";
|
|
32
|
+
import { useShortcutProps } from "../../shared/keyboard/useShortcutProps.js";
|
|
33
|
+
import { notifyApiError } from "../../shared/notifications/apiErrors.js";
|
|
29
34
|
|
|
30
35
|
const INPUT_CLASS =
|
|
31
36
|
"text-sm py-1.5 px-2 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
function TasksPage(props: TasksShellProps) {
|
|
34
39
|
return (
|
|
35
40
|
<TasksShell {...props}>
|
|
36
41
|
<TasksContent />
|
|
@@ -103,6 +108,14 @@ function TasksContent() {
|
|
|
103
108
|
// checked task (the toolbar's Delete).
|
|
104
109
|
const [pendingDelete, setPendingDelete] = useState<{ type: "single"; task: Task } | { type: "bulk" } | null>(null);
|
|
105
110
|
|
|
111
|
+
// "New task" from the keyboard: a task is made in the quick-add form, so this moves the caret to its title field (which the Flagged view,
|
|
112
|
+
// a list of flagged mail rather than tasks, doesn't have). A dialog open - the delete confirmation - silences it.
|
|
113
|
+
const addTaskRef = useRef<HTMLInputElement | null>(null);
|
|
114
|
+
const canAddTask = view.type !== "flagged";
|
|
115
|
+
// Registered only while the form is rendered (`canAddTask`), so its field is there.
|
|
116
|
+
useShortcut(SHORTCUTS.tasks.create, () => addTaskRef.current!.focus(), { enabled: canAddTask });
|
|
117
|
+
const addTaskHint = useShortcutProps("Add a task", SHORTCUTS.tasks.create, canAddTask);
|
|
118
|
+
|
|
106
119
|
function reload(): Promise<void> {
|
|
107
120
|
if (!folderUid) {
|
|
108
121
|
setTasks([]);
|
|
@@ -186,7 +199,6 @@ function TasksContent() {
|
|
|
186
199
|
}
|
|
187
200
|
|
|
188
201
|
async function handleToggle(task: Task) {
|
|
189
|
-
setError(null);
|
|
190
202
|
const nextCompleted = !task.completed;
|
|
191
203
|
setTasks((prev) => prev.map((t) => (t.uid === task.uid ? { ...t, completed: nextCompleted } : t)));
|
|
192
204
|
try {
|
|
@@ -194,17 +206,16 @@ function TasksContent() {
|
|
|
194
206
|
setTasks((prev) => prev.map((t) => (t.uid === task.uid ? updated : t)));
|
|
195
207
|
} catch (err) {
|
|
196
208
|
setTasks((prev) => prev.map((t) => (t.uid === task.uid ? task : t)));
|
|
197
|
-
|
|
209
|
+
notifyApiError(err, "Couldn't update the task");
|
|
198
210
|
}
|
|
199
211
|
}
|
|
200
212
|
|
|
201
213
|
async function handleDelete(task: Task) {
|
|
202
|
-
setError(null);
|
|
203
214
|
try {
|
|
204
215
|
await deleteTask(task.uid, task.version);
|
|
205
216
|
setTasks((prev) => prev.filter((t) => t.uid !== task.uid));
|
|
206
217
|
} catch (err) {
|
|
207
|
-
|
|
218
|
+
notifyApiError(err, "Couldn't delete the task");
|
|
208
219
|
}
|
|
209
220
|
}
|
|
210
221
|
|
|
@@ -246,46 +257,41 @@ function TasksContent() {
|
|
|
246
257
|
|
|
247
258
|
const checkedTasks = viewFiltered.filter((t) => checkedUids.has(t.uid));
|
|
248
259
|
|
|
260
|
+
// The bulk handlers try every task and report each failure: the same failure repeated is one pop-up with a count.
|
|
249
261
|
async function handleBulkComplete() {
|
|
250
|
-
let bulkError: string | null = null;
|
|
251
262
|
for (const task of checkedTasks) {
|
|
252
263
|
try {
|
|
253
264
|
await setTaskCompleted(task, true);
|
|
254
265
|
} catch (err) {
|
|
255
|
-
|
|
266
|
+
notifyApiError(err, "Couldn't update some of the tasks");
|
|
256
267
|
}
|
|
257
268
|
}
|
|
258
269
|
setCheckedUids(new Set());
|
|
259
270
|
await reload();
|
|
260
|
-
setError(bulkError);
|
|
261
271
|
}
|
|
262
272
|
|
|
263
273
|
async function handleBulkAddToMyDay() {
|
|
264
|
-
let bulkError: string | null = null;
|
|
265
274
|
for (const task of checkedTasks) {
|
|
266
275
|
try {
|
|
267
276
|
await setTaskMyDay(task, true);
|
|
268
277
|
} catch (err) {
|
|
269
|
-
|
|
278
|
+
notifyApiError(err, "Couldn't update some of the tasks");
|
|
270
279
|
}
|
|
271
280
|
}
|
|
272
281
|
setCheckedUids(new Set());
|
|
273
282
|
await reload();
|
|
274
|
-
setError(bulkError);
|
|
275
283
|
}
|
|
276
284
|
|
|
277
285
|
async function handleBulkDelete() {
|
|
278
|
-
let bulkError: string | null = null;
|
|
279
286
|
for (const task of checkedTasks) {
|
|
280
287
|
try {
|
|
281
288
|
await deleteTask(task.uid, task.version);
|
|
282
289
|
} catch (err) {
|
|
283
|
-
|
|
290
|
+
notifyApiError(err, "Couldn't delete some of the tasks");
|
|
284
291
|
}
|
|
285
292
|
}
|
|
286
293
|
setCheckedUids(new Set());
|
|
287
294
|
await reload();
|
|
288
|
-
setError(bulkError);
|
|
289
295
|
}
|
|
290
296
|
|
|
291
297
|
function requestDelete(task: Task) {
|
|
@@ -317,7 +323,8 @@ function TasksContent() {
|
|
|
317
323
|
const completedTasks = viewFiltered.filter((t) => t.completed);
|
|
318
324
|
|
|
319
325
|
return (
|
|
320
|
-
|
|
326
|
+
// Below `lg` the tasks menu is a drawer (its button sits above the list), so the list keeps the whole width.
|
|
327
|
+
<div className="flex-1 min-w-0 min-h-0 flex flex-col lg:flex-row">
|
|
321
328
|
<TasksSidebar mailboxUid={mailboxUid} tasks={tasks} userUid={userUid} active={view} onSelect={handleSelectView} />
|
|
322
329
|
<div className="flex-1 min-w-0 flex flex-col overflow-y-auto">
|
|
323
330
|
<TasksToolbar
|
|
@@ -342,11 +349,13 @@ function TasksContent() {
|
|
|
342
349
|
<FlaggedEmailList loading={flaggedLoading} error={flaggedError} messages={flaggedMessages} />
|
|
343
350
|
) : (
|
|
344
351
|
<>
|
|
345
|
-
<form onSubmit={handleCreate} className="flex items-center gap-2 bg-surface border border-border rounded-md p-2">
|
|
352
|
+
<form onSubmit={handleCreate} className="flex flex-wrap items-center gap-2 bg-surface border border-border rounded-md p-2">
|
|
346
353
|
<input
|
|
354
|
+
ref={addTaskRef}
|
|
347
355
|
type="text"
|
|
348
356
|
aria-label="Add a task"
|
|
349
|
-
|
|
357
|
+
{...addTaskHint}
|
|
358
|
+
className={`${INPUT_CLASS} min-w-40 basis-40 flex-1`}
|
|
350
359
|
value={title}
|
|
351
360
|
onChange={(e) => setTitle(e.target.value)}
|
|
352
361
|
placeholder="Add a task"
|
|
@@ -636,3 +645,5 @@ function TaskTable({ tasks, checkedUids, onToggleChecked, onToggle, onDelete }:
|
|
|
636
645
|
</div>
|
|
637
646
|
);
|
|
638
647
|
}
|
|
648
|
+
|
|
649
|
+
export default routedPage("/tasks", TasksPage);
|
|
@@ -4,5 +4,9 @@ export interface LayoutProps {
|
|
|
4
4
|
/** See `apps/www/_layout.tsx`'s `LayoutProps` doc comment - identical mechanism, supplied by
|
|
5
5
|
* `AdminConsoleRoute`'s `fetchProps()` override. */
|
|
6
6
|
branding?: Branding;
|
|
7
|
+
/** The signed-in user's stored appearance preferences (a page prop from the same `fetchProps()`, present only on the www pages and only when the user has any): rendered as a stylesheet in the `<head>` so the very first paint is themed - see `AppearanceHead`. */
|
|
8
|
+
appearance?: unknown;
|
|
9
|
+
/** The signed-in user (supplied by the framework), so a cached appearance from another account on this browser is never applied. */
|
|
10
|
+
userUid?: string;
|
|
7
11
|
}
|
|
8
|
-
export default function Layout({ children, branding }: PropsWithChildren<LayoutProps>): React.JSX.Element;
|
|
12
|
+
export default function Layout({ children, branding, appearance, userUid }: PropsWithChildren<LayoutProps>): React.JSX.Element;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { AppearanceHead, appearanceHtmlAttributes } from "../shared/appearance/AppearanceHead.js";
|
|
2
3
|
import { CUSTOM_STYLESHEET_LINK_ID } from "@rapidmx/react-shared/branding/useBranding.js";
|
|
3
|
-
export default function Layout({ children, branding }) {
|
|
4
|
+
export default function Layout({ children, branding, appearance, userUid }) {
|
|
4
5
|
const title = branding?.title || branding?.companyName
|
|
5
6
|
? `${branding?.title || branding?.companyName}: Mail Admin Console`
|
|
6
7
|
: "RapidMX: Mail Admin Console";
|
|
7
8
|
const iconHref = branding?.iconUrl || branding?.logoUrl || "/images/logo.svg";
|
|
8
9
|
const stylesheetHref = branding?.stylesheetUrl;
|
|
9
|
-
return (_jsxs("html", { lang: "en", children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("title", { children: title }), _jsx("link", { rel: "icon", type: "image/svg+xml", href: iconHref }), _jsx("link", { rel: "alternate icon", href: "/favicon.ico" }), _jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }), _jsx("link", { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" }), stylesheetHref && _jsx("link", { rel: "stylesheet", href: stylesheetHref, id: CUSTOM_STYLESHEET_LINK_ID })] }), _jsx("body", { children: children })] }));
|
|
10
|
+
return (_jsxs("html", { lang: "en", ...appearanceHtmlAttributes(appearance, userUid), children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("title", { children: title }), _jsx("link", { rel: "icon", type: "image/svg+xml", href: iconHref }), _jsx("link", { rel: "alternate icon", href: "/favicon.ico" }), _jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }), _jsx("link", { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" }), stylesheetHref && _jsx("link", { rel: "stylesheet", href: stylesheetHref, id: CUSTOM_STYLESHEET_LINK_ID }), _jsx(AppearanceHead, { appearance: appearance })] }), _jsx("body", { children: children })] }));
|
|
10
11
|
}
|
package/dist/apps/admin/index.js
CHANGED
|
@@ -24,7 +24,8 @@ function MailboxesListContent() {
|
|
|
24
24
|
useEffect(() => {
|
|
25
25
|
setLoading(true);
|
|
26
26
|
setError(null);
|
|
27
|
-
|
|
27
|
+
// The administration scope: every mailbox as administrative metadata - the console never shows anybody's mail.
|
|
28
|
+
listMailboxes({ page, limit: PAGE_SIZE, scope: "admin" })
|
|
28
29
|
.then(setMailboxes)
|
|
29
30
|
.catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load mailboxes."))
|
|
30
31
|
.finally(() => setLoading(false));
|
|
@@ -44,5 +45,5 @@ function MailboxesListContent() {
|
|
|
44
45
|
setConfirmingSetup(false);
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-between mb-5", children: [_jsx("h1", { className: "text-xl font-bold uppercase tracking-wide", children: "Mailboxes" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: reopening, onClick: () => setConfirmingSetup(true), children: "Run setup again" }), _jsx("a", { href: "/admin/mailboxes/new", children: _jsx(Button, { type: "button", className: "!w-auto", children: "+ New mailbox" }) })] })] }), error && _jsx(Alert, { children: error }), _jsxs(Modal, { open: confirmingSetup, onClose: () => setConfirmingSetup(false), title: "Run setup again?", children: [_jsx("p", { className: "text-sm mb-4", children: "Setup starts again from its first step, and the admin console sends administrators to it until it's finished. Your existing settings, domains and mailboxes are kept." }), _jsxs("div", { className: "flex gap-2 justify-end", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingSetup(false), children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: reopening, disabled: reopening, onClick: () => void handleRunSetup(), children: "Run setup" })] })] }), loading ? (_jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" })) : (_jsx(MailboxTable, { mailboxes: mailboxes })), _jsxs("div", { className: "flex gap-3 items-center mt-4", children: [_jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: page === 0 || loading, onClick: () => setPage((p) => Math.max(0, p - 1)), children: "Previous" }), _jsxs("span", { className: "text-sm text-text-muted", children: ["Page ", page + 1] }), _jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: !hasNextPage || loading, onClick: () => setPage((p) => p + 1), children: "Next" })] })] }));
|
|
48
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-between mb-5", children: [_jsx("h1", { className: "text-xl font-bold uppercase tracking-wide", children: "Mailboxes" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: reopening, onClick: () => setConfirmingSetup(true), children: "Run setup again" }), _jsx("a", { href: "/admin/mailboxes/new", children: _jsx(Button, { type: "button", className: "!w-auto", children: "+ New mailbox" }) })] })] }), _jsx("p", { className: "text-sm text-text-muted mb-4", children: "Administrative details only (addresses, owners, quota). The console never shows a mailbox\u2019s mail: to see someone\u2019s account, impersonate them from their mailbox page." }), error && _jsx(Alert, { children: error }), _jsxs(Modal, { open: confirmingSetup, onClose: () => setConfirmingSetup(false), title: "Run setup again?", children: [_jsx("p", { className: "text-sm mb-4", children: "Setup starts again from its first step, and the admin console sends administrators to it until it's finished. Your existing settings, domains and mailboxes are kept." }), _jsxs("div", { className: "flex gap-2 justify-end", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingSetup(false), children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: reopening, disabled: reopening, onClick: () => void handleRunSetup(), children: "Run setup" })] })] }), loading ? (_jsx("p", { className: "text-sm text-text-muted", children: "Loading\u2026" })) : (_jsx(MailboxTable, { mailboxes: mailboxes })), _jsxs("div", { className: "flex gap-3 items-center mt-4", children: [_jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: page === 0 || loading, onClick: () => setPage((p) => Math.max(0, p - 1)), children: "Previous" }), _jsxs("span", { className: "text-sm text-text-muted", children: ["Page ", page + 1] }), _jsx(Button, { variant: "secondary", type: "button", className: "!w-auto", disabled: !hasNextPage || loading, onClick: () => setPage((p) => p + 1), children: "Next" })] })] }));
|
|
48
49
|
}
|
|
@@ -41,7 +41,8 @@ function IngestQueueContent() {
|
|
|
41
41
|
let cancelled = false;
|
|
42
42
|
setLoading(true);
|
|
43
43
|
setError(null);
|
|
44
|
-
|
|
44
|
+
// The administration scope: any mailbox's pending mail, recorded in the audit log.
|
|
45
|
+
listIngestQueue(mailboxUid, { page, limit: PAGE_SIZE, scope: "admin" })
|
|
45
46
|
.then((data) => {
|
|
46
47
|
if (!cancelled)
|
|
47
48
|
setEntries(data);
|
|
@@ -23,9 +23,9 @@ function formatBytes(bytes) {
|
|
|
23
23
|
return `${bytes} B`;
|
|
24
24
|
}
|
|
25
25
|
export default function MailboxDetailPage(props) {
|
|
26
|
-
return (_jsx(AdminShell, { ...props, active: "mailboxes", children: _jsx(MailboxDetailContent, { uid: props.params.uid, impersonationBaseUrl: props.impersonationBaseUrl }) }));
|
|
26
|
+
return (_jsx(AdminShell, { ...props, active: "mailboxes", children: _jsx(MailboxDetailContent, { uid: props.params.uid, impersonationBaseUrl: props.impersonationBaseUrl, currentUserUid: props.userUid }) }));
|
|
27
27
|
}
|
|
28
|
-
function MailboxDetailContent({ uid, impersonationBaseUrl }) {
|
|
28
|
+
function MailboxDetailContent({ uid, impersonationBaseUrl, currentUserUid, }) {
|
|
29
29
|
const [mailbox, setMailbox] = useState(null);
|
|
30
30
|
const [loading, setLoading] = useState(true);
|
|
31
31
|
const [error, setError] = useState(null);
|
|
@@ -43,7 +43,7 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
|
|
|
43
43
|
setConfirmingAccess(false);
|
|
44
44
|
setAccessError(null);
|
|
45
45
|
}
|
|
46
|
-
// Only ever invoked from the
|
|
46
|
+
// Only ever invoked from the impersonation-confirmation modal below, which only renders for a loaded mailbox that has
|
|
47
47
|
// an owner. A failure stays in the modal rather than replacing the whole page.
|
|
48
48
|
async function handleAccessMailbox() {
|
|
49
49
|
setImpersonating(true);
|
|
@@ -53,7 +53,7 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
|
|
|
53
53
|
window.location.href = "/";
|
|
54
54
|
}
|
|
55
55
|
catch (err) {
|
|
56
|
-
setAccessError(err instanceof ApiRequestError ? err.message : "Could not
|
|
56
|
+
setAccessError(err instanceof ApiRequestError ? err.message : "Could not impersonate this user.");
|
|
57
57
|
setImpersonating(false);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -82,7 +82,8 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
|
|
|
82
82
|
useEffect(() => {
|
|
83
83
|
setLoading(true);
|
|
84
84
|
setError(null);
|
|
85
|
-
|
|
85
|
+
// The administration scope: administrative metadata only - the console never shows a mailbox's mail or settings.
|
|
86
|
+
getMailbox(uid, { scope: "admin" })
|
|
86
87
|
.then(setMailbox)
|
|
87
88
|
.catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load this mailbox."))
|
|
88
89
|
.finally(() => setLoading(false));
|
|
@@ -95,5 +96,7 @@ function MailboxDetailContent({ uid, impersonationBaseUrl }) {
|
|
|
95
96
|
}
|
|
96
97
|
const quarantineHref = `/admin/quarantine?mailboxUid=${encodeURIComponent(mailbox.uid)}`;
|
|
97
98
|
const ingestQueueHref = `/admin/ingest-queue?mailboxUid=${encodeURIComponent(mailbox.uid)}`;
|
|
98
|
-
return (_jsxs("div", { className: "max-w-3xl flex flex-col gap-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsx("a", { href: "/admin", className: "text-sm text-primary-dark hover:underline", children: "\u2190 All mailboxes" }), _jsx("h1", { className: "text-xl font-bold tracking-tight mt-1", children: mailbox.primarySmtpAddress })] }), _jsxs("div", { className: "flex gap-3 shrink-0", children: [mailbox.ownerUserUid && (_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingAccess(true), children: "
|
|
99
|
+
return (_jsxs("div", { className: "max-w-3xl flex flex-col gap-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsx("a", { href: "/admin", className: "text-sm text-primary-dark hover:underline", children: "\u2190 All mailboxes" }), _jsx("h1", { className: "text-xl font-bold tracking-tight mt-1", children: mailbox.primarySmtpAddress })] }), _jsxs("div", { className: "flex gap-3 shrink-0", children: [mailbox.ownerUserUid && (_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => setConfirmingAccess(true), children: "Impersonate this user" })), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto !border-danger !text-danger hover:!border-danger hover:!text-danger", onClick: () => setConfirmingDelete(true), children: "Delete mailbox" })] })] }), _jsxs("p", { className: "text-sm text-text-muted", children: ["The console shows administrative details only - never a mailbox\u2019s mail or settings.", mailbox.ownerUserUid
|
|
100
|
+
? " To see this mailbox as its owner sees it, impersonate them: that is recorded, and you can stop at any time."
|
|
101
|
+
: " This shared mailbox has no owner: add yourself under Shared access to open it in the mail client."] }), _jsxs("div", { className: "bg-surface border border-border rounded-md p-6", children: [_jsxs("dl", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-3 text-sm", children: [_jsx("dt", { className: "text-text-muted", children: "Display name" }), _jsx("dd", { children: mailbox.displayName }), _jsx("dt", { className: "text-text-muted", children: "Owner" }), _jsx("dd", { children: mailbox.ownerUserUid ?? "None (shared mailbox)" }), _jsx("dt", { className: "text-text-muted", children: "Timezone" }), _jsx("dd", { children: mailbox.timezone }), _jsx("dt", { className: "text-text-muted", children: "Quota" }), _jsxs("dd", { children: [formatBytes(mailbox.usedBytes), " / ", formatBytes(mailbox.quotaBytes)] }), _jsx("dt", { className: "text-text-muted", children: "Alias addresses" }), _jsx("dd", { children: mailbox.aliasAddresses.length > 0 ? mailbox.aliasAddresses.join(", ") : "None" }), mailbox.isResource && (_jsxs(_Fragment, { children: [_jsx("dt", { className: "text-text-muted", children: "Resource type" }), _jsx("dd", { className: "capitalize", children: mailbox.resourceType ?? "room" })] })), _jsx("dt", { className: "text-text-muted", children: "Created" }), _jsx("dd", { children: new Date(mailbox.dateCreated).toLocaleString() })] }), _jsxs("div", { className: "flex gap-4 mt-5 pt-5 border-t border-border text-sm font-medium", children: [_jsx("a", { href: quarantineHref, className: "text-primary-dark hover:underline", children: "View quarantine" }), _jsx("a", { href: ingestQueueHref, className: "text-primary-dark hover:underline", children: "View ingest queue" })] })] }), _jsx(ShareAccessCard, { mailboxUid: mailbox.uid, ownerUserUid: mailbox.ownerUserUid, currentUserUid: currentUserUid }), _jsx(EscrowScopeCard, { mailbox: mailbox, onUpdate: setMailbox }), mailbox.isResource && _jsx(ResourceSettingsCard, { mailbox: mailbox, onUpdate: setMailbox }), _jsxs(Modal, { open: confirmingAccess, onClose: closeAccessModal, title: "Impersonate this user", children: [_jsxs("p", { className: "text-sm mb-3", children: ["You\u2019ll be signed in as ", _jsx("strong", { className: "break-all", children: mailbox.ownerUserUid }), ", the owner of ", _jsx("strong", { className: "break-all", children: mailbox.primarySmtpAddress }), ", and see everything they can - and act as them, until you stop impersonating. This is the only way an administrator sees another user\u2019s mail."] }), accessError && _jsx(Alert, { children: accessError }), _jsxs("div", { className: "flex gap-3 justify-end mt-5", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: impersonating, onClick: closeAccessModal, children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto", loading: impersonating, disabled: impersonating, onClick: handleAccessMailbox, children: "Impersonate" })] })] }), _jsxs(Modal, { open: confirmingDelete, onClose: closeDeleteModal, title: "Delete mailbox", children: [_jsxs("p", { className: "text-sm mb-5", children: ["Are you sure you want to delete ", _jsx("strong", { children: mailbox.primarySmtpAddress }), "? This permanently deletes the mailbox and everything in it, and cannot be undone. It fails if this mailbox is a custodian on an active legal hold."] }), deleteError && _jsx(Alert, { children: deleteError }), _jsxs("div", { className: "flex gap-3 justify-end mt-5", children: [_jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", disabled: deleting, onClick: closeDeleteModal, children: "Cancel" }), _jsx(Button, { type: "button", className: "!w-auto !bg-none !bg-danger !border-danger hover:!bg-danger", loading: deleting, disabled: deleting, onClick: handleDelete, children: "Delete" })] })] })] }));
|
|
99
102
|
}
|
|
@@ -34,7 +34,8 @@ function QuarantineContent({ userUid }) {
|
|
|
34
34
|
function reload(uid) {
|
|
35
35
|
setLoading(true);
|
|
36
36
|
setError(null);
|
|
37
|
-
|
|
37
|
+
// The administration scope: any mailbox's held mail, recorded in the audit log.
|
|
38
|
+
listQuarantine(uid, { page, limit: PAGE_SIZE, scope: "admin" })
|
|
38
39
|
.then(setEntries)
|
|
39
40
|
.catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load quarantine."))
|
|
40
41
|
.finally(() => setLoading(false));
|