@rapidmx/web-client 0.4.0 → 0.6.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 +88 -0
- package/apps/admin/audit-log/index.tsx +35 -8
- package/apps/admin/data-requests/index.tsx +480 -485
- package/apps/admin/distribution-lists/[uid].tsx +19 -1
- package/apps/admin/escrow-scopes/[uid].tsx +348 -237
- package/apps/admin/escrow-scopes/new/index.tsx +8 -2
- package/apps/admin/index.tsx +19 -1
- package/apps/admin/ingest-queue/index.tsx +44 -5
- package/apps/admin/mailboxes/[uid].tsx +211 -184
- package/apps/admin/quarantine/index.tsx +46 -14
- package/apps/admin/transport-rules/[uid].tsx +30 -1
- package/apps/admin/transport-rules/_transportRuleConfig.tsx +38 -2
- package/apps/admin/transport-rules/index.tsx +24 -6
- package/apps/admin/transport-rules/new/index.tsx +30 -1
- package/apps/escrow/_layout.tsx +2 -3
- package/apps/escrow/audit-log/index.tsx +196 -168
- package/apps/escrow/matters/[uid].tsx +621 -521
- package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +22 -1
- package/apps/shared/components/admin/layout/AdminShell.tsx +28 -9
- package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +152 -0
- package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +3 -3
- package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +55 -11
- package/apps/shared/components/admin/settings/BrandingForm.tsx +15 -4
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +18 -7
- package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +10 -2
- package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +6 -0
- package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +45 -12
- package/apps/shared/components/admin/settings/PluginsManager.tsx +1235 -595
- package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +174 -98
- package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +49 -13
- package/apps/shared/components/admin/setup/SetupWizard.tsx +150 -34
- package/apps/shared/components/admin/signOut.ts +79 -0
- package/apps/shared/components/admin/usePagedList.tsx +129 -0
- package/apps/shared/components/calendar/EventModal.tsx +717 -549
- package/apps/shared/components/calendar/MonthView.tsx +173 -139
- package/apps/shared/components/calendar/RecurrenceEditor.tsx +222 -180
- package/apps/shared/components/calendar/SplitDayView.tsx +143 -137
- package/apps/shared/components/calendar/TimeGridView.tsx +244 -198
- package/apps/shared/components/calendar/allDay.ts +124 -0
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +194 -192
- package/apps/shared/components/contacts/ContactDetailPane.tsx +100 -21
- package/apps/shared/components/contacts/ContactForm.tsx +377 -348
- package/apps/shared/components/contacts/ContactsSidebar.tsx +6 -2
- package/apps/shared/components/contacts/KeyChangeReview.tsx +192 -0
- package/apps/shared/components/contacts/contactKeys.ts +78 -0
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +199 -197
- package/apps/shared/components/escrow/layout/EscrowShell.tsx +148 -134
- package/apps/shared/components/layout/AppShell.tsx +123 -33
- package/apps/shared/components/layout/BrandingChrome.tsx +48 -10
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +389 -298
- package/apps/shared/components/layout/MailboxProvisioning.tsx +19 -3
- package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +350 -0
- package/apps/shared/components/layout/UnlockPromptProvider.tsx +257 -128
- package/apps/shared/components/mail/ConversationThreadPane.tsx +216 -181
- package/apps/shared/components/mail/MessageDetailPane.tsx +1318 -630
- package/apps/shared/components/mail/compose/ComposeContext.tsx +20 -15
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +854 -132
- package/apps/shared/components/mail/compose/composeFlushRegistry.ts +60 -0
- package/apps/shared/components/mail/layout/MailShell.tsx +420 -408
- package/apps/shared/components/mail/pinnedSigners.ts +124 -0
- package/apps/shared/components/mail/verificationSeals.ts +125 -0
- package/apps/shared/components/mail/writableMailboxes.ts +101 -0
- package/apps/shared/components/rules/RuleBuilder.tsx +311 -276
- package/apps/shared/components/settings/layout/SettingsShell.tsx +23 -9
- package/apps/shared/components/tasks/layout/TasksShell.tsx +201 -199
- package/apps/shared/mail/listAllPages.ts +39 -0
- package/apps/shared/plugins/pluginNav.ts +73 -0
- package/apps/shared/search/LocalIndexLifecycle.tsx +64 -55
- package/apps/shared/search/localIndexBuilder.ts +481 -179
- package/apps/shared/search/localIndexRpcClient.ts +231 -29
- package/apps/shared/search/localIndexSchema.ts +15 -6
- package/apps/shared/search/localIndexStorage.ts +98 -0
- package/apps/shared/search/localIndexVFS.ts +32 -1
- package/apps/shared/search/localIndexWorker.ts +648 -186
- package/apps/www/calendar/index.tsx +438 -417
- package/apps/www/contacts/[uid].tsx +21 -2
- package/apps/www/contacts/index.tsx +116 -12
- package/apps/www/index.tsx +1314 -1041
- package/apps/www/messages/[uid].tsx +18 -6
- package/apps/www/settings/auto-reply/index.tsx +134 -129
- package/apps/www/settings/encryption/index.tsx +739 -216
- package/apps/www/settings/filters/[uid].tsx +171 -155
- package/apps/www/settings/filters/new/index.tsx +8 -1
- package/apps/www/settings/focused-inbox/index.tsx +150 -148
- package/apps/www/settings/privacy/index.tsx +494 -436
- package/apps/www/settings/read-receipts/index.tsx +148 -125
- package/apps/www/settings/sharing/index.tsx +17 -14
- package/apps/www/tasks/index.tsx +638 -579
- package/dist/apps/admin/_404.d.ts +2 -0
- package/dist/apps/admin/_500.d.ts +2 -0
- package/dist/apps/admin/_layout.d.ts +8 -0
- package/dist/apps/admin/audit-log/index.d.ts +11 -0
- package/dist/apps/admin/audit-log/index.js +38 -8
- package/dist/apps/admin/branding/index.d.ts +3 -0
- package/dist/apps/admin/data-requests/index.d.ts +3 -0
- package/dist/apps/admin/data-requests/index.js +52 -72
- package/dist/apps/admin/distribution-lists/[uid].d.ts +7 -0
- package/dist/apps/admin/distribution-lists/[uid].js +1 -1
- package/dist/apps/admin/distribution-lists/index.d.ts +3 -0
- package/dist/apps/admin/distribution-lists/new/index.d.ts +3 -0
- package/dist/apps/admin/domains/[uid].d.ts +7 -0
- package/dist/apps/admin/domains/index.d.ts +3 -0
- package/dist/apps/admin/domains/new/index.d.ts +3 -0
- package/dist/apps/admin/encryption-policy/index.d.ts +3 -0
- package/dist/apps/admin/escrow-scopes/[uid].d.ts +21 -0
- package/dist/apps/admin/escrow-scopes/[uid].js +91 -12
- package/dist/apps/admin/escrow-scopes/index.d.ts +3 -0
- package/dist/apps/admin/escrow-scopes/new/index.d.ts +3 -0
- package/dist/apps/admin/escrow-scopes/new/index.js +8 -3
- package/dist/apps/admin/index.d.ts +3 -0
- package/dist/apps/admin/index.js +4 -1
- package/dist/apps/admin/ingest-queue/index.d.ts +4 -0
- package/dist/apps/admin/ingest-queue/index.js +24 -6
- package/dist/apps/admin/mailbox-policy/index.d.ts +3 -0
- package/dist/apps/admin/mailboxes/[uid].d.ts +7 -0
- package/dist/apps/admin/mailboxes/[uid].js +20 -4
- package/dist/apps/admin/mailboxes/new/index.d.ts +3 -0
- package/dist/apps/admin/plugins/index.d.ts +3 -0
- package/dist/apps/admin/quarantine/index.d.ts +6 -0
- package/dist/apps/admin/quarantine/index.js +20 -11
- package/dist/apps/admin/retention-policy/index.d.ts +3 -0
- package/dist/apps/admin/setup/index.d.ts +3 -0
- package/dist/apps/admin/transport-rules/[uid].d.ts +7 -0
- package/dist/apps/admin/transport-rules/[uid].js +18 -2
- package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +21 -0
- package/dist/apps/admin/transport-rules/_transportRuleConfig.js +19 -0
- package/dist/apps/admin/transport-rules/index.d.ts +3 -0
- package/dist/apps/admin/transport-rules/index.js +22 -6
- package/dist/apps/admin/transport-rules/new/index.d.ts +3 -0
- package/dist/apps/admin/transport-rules/new/index.js +18 -2
- package/dist/apps/escrow/_layout.d.ts +10 -0
- package/dist/apps/escrow/_layout.js +3 -3
- package/dist/apps/escrow/audit-log/index.d.ts +9 -0
- package/dist/apps/escrow/audit-log/index.js +26 -1
- package/dist/apps/escrow/index.d.ts +3 -0
- package/dist/apps/escrow/matters/[uid].d.ts +7 -0
- package/dist/apps/escrow/matters/[uid].js +77 -42
- package/dist/apps/escrow/matters/new/index.d.ts +3 -0
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +17 -0
- package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.d.ts +32 -0
- package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +11 -2
- package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +32 -0
- package/dist/apps/shared/components/admin/layout/AdminShell.js +15 -6
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +12 -0
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +72 -0
- package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +6 -0
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +19 -0
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +3 -3
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.d.ts +12 -0
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +22 -4
- package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +9 -0
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +15 -4
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +12 -0
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +13 -7
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +12 -0
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +7 -2
- package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.d.ts +9 -0
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +19 -0
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +6 -0
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +10 -0
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +34 -12
- package/dist/apps/shared/components/admin/settings/PluginsManager.d.ts +4 -0
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +390 -78
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +9 -0
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +40 -11
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.d.ts +14 -0
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +29 -13
- package/dist/apps/shared/components/admin/setup/SetupWizard.d.ts +36 -0
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +87 -21
- package/dist/apps/shared/components/admin/signOut.d.ts +23 -0
- package/dist/apps/shared/components/admin/signOut.js +75 -0
- package/dist/apps/shared/components/admin/usePagedList.d.ts +39 -0
- package/dist/apps/shared/components/admin/usePagedList.js +99 -0
- package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +26 -0
- package/dist/apps/shared/components/calendar/EventModal.d.ts +46 -0
- package/dist/apps/shared/components/calendar/EventModal.js +142 -21
- package/dist/apps/shared/components/calendar/MonthView.d.ts +15 -0
- package/dist/apps/shared/components/calendar/MonthView.js +12 -5
- package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +20 -0
- package/dist/apps/shared/components/calendar/RecurrenceEditor.js +40 -5
- package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +19 -0
- package/dist/apps/shared/components/calendar/SplitDayView.d.ts +28 -0
- package/dist/apps/shared/components/calendar/SplitDayView.js +10 -4
- package/dist/apps/shared/components/calendar/TimeGridView.d.ts +17 -0
- package/dist/apps/shared/components/calendar/TimeGridView.js +23 -7
- package/dist/apps/shared/components/calendar/allDay.d.ts +51 -0
- package/dist/apps/shared/components/calendar/allDay.js +111 -0
- package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +40 -0
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +3 -3
- package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +23 -0
- package/dist/apps/shared/components/contacts/ContactDetailPane.js +43 -5
- package/dist/apps/shared/components/contacts/ContactForm.d.ts +20 -0
- package/dist/apps/shared/components/contacts/ContactForm.js +32 -10
- package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +40 -0
- package/dist/apps/shared/components/contacts/ContactsSidebar.js +2 -2
- package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +25 -0
- package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +40 -0
- package/dist/apps/shared/components/contacts/KeyChangeReview.js +63 -0
- package/dist/apps/shared/components/contacts/contactKeys.d.ts +29 -0
- package/dist/apps/shared/components/contacts/contactKeys.js +64 -0
- package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +21 -0
- package/dist/apps/shared/components/contacts/layout/ContactsShell.js +2 -2
- package/dist/apps/shared/components/escrow/layout/EscrowShell.d.ts +25 -0
- package/dist/apps/shared/components/escrow/layout/EscrowShell.js +24 -5
- package/dist/apps/shared/components/forms/StringListField.d.ts +22 -0
- package/dist/apps/shared/components/layout/AppShell.d.ts +49 -0
- package/dist/apps/shared/components/layout/AppShell.js +97 -26
- package/dist/apps/shared/components/layout/BrandingChrome.d.ts +18 -0
- package/dist/apps/shared/components/layout/BrandingChrome.js +45 -10
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +51 -0
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +49 -12
- package/dist/apps/shared/components/layout/MailboxProvisioning.d.ts +13 -0
- package/dist/apps/shared/components/layout/MailboxProvisioning.js +10 -2
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +66 -0
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +174 -0
- package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +45 -0
- package/dist/apps/shared/components/layout/UnlockPromptProvider.js +73 -14
- package/dist/apps/shared/components/layout/UserMenu.d.ts +23 -0
- package/dist/apps/shared/components/mail/ConversationList.d.ts +11 -0
- package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +23 -0
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +50 -12
- package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +93 -0
- package/dist/apps/shared/components/mail/MessageDetailPane.js +441 -36
- package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +58 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +9 -6
- package/dist/apps/shared/components/mail/compose/ComposeToolbar.d.ts +15 -0
- package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +37 -0
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +699 -128
- package/dist/apps/shared/components/mail/compose/EmojiPicker.d.ts +14 -0
- package/dist/apps/shared/components/mail/compose/GifPicker.d.ts +13 -0
- package/dist/apps/shared/components/mail/compose/RichTextEditor.d.ts +34 -0
- package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.d.ts +16 -0
- package/dist/apps/shared/components/mail/compose/composeFlushRegistry.d.ts +27 -0
- package/dist/apps/shared/components/mail/compose/composeFlushRegistry.js +50 -0
- package/dist/apps/shared/components/mail/layout/MailShell.d.ts +57 -0
- package/dist/apps/shared/components/mail/layout/MailShell.js +13 -4
- package/dist/apps/shared/components/mail/pinnedSigners.d.ts +33 -0
- package/dist/apps/shared/components/mail/pinnedSigners.js +98 -0
- package/dist/apps/shared/components/mail/verificationSeals.d.ts +22 -0
- package/dist/apps/shared/components/mail/verificationSeals.js +112 -0
- package/dist/apps/shared/components/mail/writableMailboxes.d.ts +15 -0
- package/dist/apps/shared/components/mail/writableMailboxes.js +87 -0
- package/dist/apps/shared/components/rules/RuleBuilder.d.ts +70 -0
- package/dist/apps/shared/components/rules/RuleBuilder.js +34 -6
- package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +41 -0
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +19 -9
- package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +41 -0
- package/dist/apps/shared/components/tasks/TasksToolbar.d.ts +14 -0
- package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +24 -0
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +2 -2
- package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -0
- package/dist/apps/shared/mail/listAllPages.d.ts +17 -0
- package/dist/apps/shared/mail/listAllPages.js +25 -0
- package/dist/apps/shared/plugins/pluginNav.d.ts +39 -0
- package/dist/apps/shared/plugins/pluginNav.js +33 -0
- package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +13 -0
- package/dist/apps/shared/search/LocalIndexLifecycle.js +58 -54
- package/dist/apps/shared/search/localIndexBlockCipher.d.ts +36 -0
- package/dist/apps/shared/search/localIndexBuilder.d.ts +85 -0
- package/dist/apps/shared/search/localIndexBuilder.js +292 -37
- package/dist/apps/shared/search/localIndexKey.d.ts +4 -0
- package/dist/apps/shared/search/localIndexRpcClient.d.ts +71 -0
- package/dist/apps/shared/search/localIndexRpcClient.js +191 -26
- package/dist/apps/shared/search/localIndexSchema.d.ts +111 -0
- package/dist/apps/shared/search/localIndexSchema.js +12 -6
- package/dist/apps/shared/search/localIndexSizePreference.d.ts +27 -0
- package/dist/apps/shared/search/localIndexStorage.d.ts +28 -0
- package/dist/apps/shared/search/localIndexStorage.js +86 -0
- package/dist/apps/shared/search/localIndexVFS.d.ts +101 -0
- package/dist/apps/shared/search/localIndexVFS.js +68 -40
- package/dist/apps/shared/search/localIndexWorker.d.ts +145 -0
- package/dist/apps/shared/search/localIndexWorker.js +519 -173
- package/dist/apps/shared/search/searchTier2.d.ts +30 -0
- package/dist/apps/www/_404.d.ts +2 -0
- package/dist/apps/www/_500.d.ts +2 -0
- package/dist/apps/www/_layout.d.ts +14 -0
- package/dist/apps/www/calendar/index.d.ts +3 -0
- package/dist/apps/www/calendar/index.js +28 -10
- package/dist/apps/www/contacts/[uid].d.ts +13 -0
- package/dist/apps/www/contacts/[uid].js +13 -2
- package/dist/apps/www/contacts/index.d.ts +3 -0
- package/dist/apps/www/contacts/index.js +80 -10
- package/dist/apps/www/index.d.ts +3 -0
- package/dist/apps/www/index.js +339 -108
- package/dist/apps/www/messages/[uid].d.ts +11 -0
- package/dist/apps/www/messages/[uid].js +18 -6
- package/dist/apps/www/settings/auto-reply/index.d.ts +4 -0
- package/dist/apps/www/settings/auto-reply/index.js +9 -4
- package/dist/apps/www/settings/encryption/index.d.ts +7 -0
- package/dist/apps/www/settings/encryption/index.js +514 -97
- package/dist/apps/www/settings/filters/[uid].d.ts +8 -0
- package/dist/apps/www/settings/filters/[uid].js +22 -7
- package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +11 -0
- package/dist/apps/www/settings/filters/index.d.ts +4 -0
- package/dist/apps/www/settings/filters/new/index.d.ts +4 -0
- package/dist/apps/www/settings/filters/new/index.js +8 -2
- package/dist/apps/www/settings/focused-inbox/index.d.ts +4 -0
- package/dist/apps/www/settings/focused-inbox/index.js +3 -1
- package/dist/apps/www/settings/labels/index.d.ts +4 -0
- package/dist/apps/www/settings/privacy/index.d.ts +4 -0
- package/dist/apps/www/settings/privacy/index.js +57 -22
- package/dist/apps/www/settings/read-receipts/index.d.ts +4 -0
- package/dist/apps/www/settings/read-receipts/index.js +10 -3
- package/dist/apps/www/settings/sharing/index.d.ts +4 -0
- package/dist/apps/www/settings/sharing/index.js +7 -11
- package/dist/apps/www/settings/signatures/[uid].d.ts +8 -0
- package/dist/apps/www/settings/signatures/index.d.ts +4 -0
- package/dist/apps/www/settings/signatures/new/index.d.ts +4 -0
- package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +10 -0
- package/dist/apps/www/tasks/index.d.ts +3 -0
- package/dist/apps/www/tasks/index.js +50 -10
- package/package.json +3 -3
- package/apps/shared/components/booking/AvailabilityEditor.tsx +0 -118
- package/apps/www/settings/booking-types/[uid].tsx +0 -300
- package/apps/www/settings/booking-types/index.tsx +0 -98
- package/apps/www/settings/booking-types/new/index.tsx +0 -212
- package/dist/apps/shared/components/booking/AvailabilityEditor.js +0 -45
- package/dist/apps/www/settings/booking-types/[uid].js +0 -129
- package/dist/apps/www/settings/booking-types/index.js +0 -29
- package/dist/apps/www/settings/booking-types/new/index.js +0 -86
|
@@ -1,134 +1,148 @@
|
|
|
1
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
-
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
-
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
-
import "../../../styles/app.css";
|
|
6
|
-
import React, { PropsWithChildren, ReactNode, useEffect, useState } from "react";
|
|
7
|
-
import { HiOutlineClipboardDocumentList, HiOutlineFolderOpen } from "react-icons/hi2";
|
|
8
|
-
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
9
|
-
import { listMatters } from "@rapidmx/react-shared/admin/mattersApi.js";
|
|
10
|
-
import { useRedirectIfUnauthenticated } from "@rapidmx/react-shared/auth/session.js";
|
|
11
|
-
import
|
|
12
|
-
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
13
|
-
import BottomTabBar, { NavItem } from "@rapidmx/react-shared/components/navigation/BottomTabBar.js";
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
export type EscrowSection = "matters" | "auditLog";
|
|
18
|
-
|
|
19
|
-
export interface EscrowShellProps {
|
|
20
|
-
active: EscrowSection;
|
|
21
|
-
userUid?: string;
|
|
22
|
-
authServerUrl?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type Status = "checking" | "error" | "authorized";
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
* `
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
import "../../../styles/app.css";
|
|
6
|
+
import React, { PropsWithChildren, ReactNode, useEffect, useState } from "react";
|
|
7
|
+
import { HiOutlineClipboardDocumentList, HiOutlineFolderOpen } from "react-icons/hi2";
|
|
8
|
+
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
9
|
+
import { listMatters } from "@rapidmx/react-shared/admin/mattersApi.js";
|
|
10
|
+
import { useRedirectIfUnauthenticated } from "@rapidmx/react-shared/auth/session.js";
|
|
11
|
+
import { getBranding } from "@rapidmx/react-shared/branding/brandingApi.js";
|
|
12
|
+
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
13
|
+
import BottomTabBar, { NavItem } from "@rapidmx/react-shared/components/navigation/BottomTabBar.js";
|
|
14
|
+
import UserMenu from "../../layout/UserMenu.js";
|
|
15
|
+
import { signOutOfConsole } from "../../admin/signOut.js";
|
|
16
|
+
|
|
17
|
+
export type EscrowSection = "matters" | "auditLog";
|
|
18
|
+
|
|
19
|
+
export interface EscrowShellProps {
|
|
20
|
+
active: EscrowSection;
|
|
21
|
+
userUid?: string;
|
|
22
|
+
authServerUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type Status = "checking" | "error" | "authorized";
|
|
26
|
+
|
|
27
|
+
const DEFAULT_ICON_SRC = "/images/logo.svg";
|
|
28
|
+
|
|
29
|
+
const NAV_ITEMS: NavItem[] = [
|
|
30
|
+
{ id: "matters", href: "/escrow", label: "Matters", icon: HiOutlineFolderOpen },
|
|
31
|
+
{ id: "auditLog", href: "/escrow/audit-log", label: "Audit Log", icon: HiOutlineClipboardDocumentList },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Gates every `apps/escrow` page behind "is this an authenticated caller at all" — deliberately **not**
|
|
36
|
+
* the same "does a canary endpoint 403 me" pattern `AdminShell` uses for the trusted `admin` role.
|
|
37
|
+
* `specs/end-to-end_encryption.md`'s "Separation of duties" makes holder-ness a per-`EscrowScope` grant
|
|
38
|
+
* (`EscrowScope.holderUserUids`), checked directly against `holderUserUids` in every restapi route that
|
|
39
|
+
* needs it (`requireEscrowHolder()`) — never a role, and never global. There is no canary endpoint a
|
|
40
|
+
* non-holder gets a clean 403 from: `GET /escrow/matters` (`BaseMatterRoute.find()`) returns `200 []` for
|
|
41
|
+
* *any* authenticated caller who holds no scope at all, the same as it would for a holder of zero matters
|
|
42
|
+
* — so unlike `AdminShell`, this shell cannot distinguish "holds nothing" from "holds no scope at all" up
|
|
43
|
+
* front, and does not try to. It only confirms the caller is signed in and the API is reachable; every
|
|
44
|
+
* actual holder-gated action (create a Matter, approve/deny a request, read material) is enforced
|
|
45
|
+
* server-side and surfaces its own 403 `ApiRequestError` on the specific page that attempted it, same as
|
|
46
|
+
* any other gated action elsewhere in this codebase. A non-holder who reaches `/escrow` simply sees an
|
|
47
|
+
* empty Matters list and gets a real error if they try to act on something they don't hold — this is a
|
|
48
|
+
* deliberate, documented scope trim rather than an oversight (see this session's own report on it).
|
|
49
|
+
*/
|
|
50
|
+
export default function EscrowShell({ active, userUid, authServerUrl, children }: PropsWithChildren<EscrowShellProps>) {
|
|
51
|
+
const [status, setStatus] = useState<Status>("checking");
|
|
52
|
+
const [error, setError] = useState<string | null>(null);
|
|
53
|
+
const [iconSrc, setIconSrc] = useState(DEFAULT_ICON_SRC);
|
|
54
|
+
|
|
55
|
+
useRedirectIfUnauthenticated(userUid, authServerUrl);
|
|
56
|
+
|
|
57
|
+
// Only the icon is taken from branding here. The escrow console deliberately skips `useBranding()`, which
|
|
58
|
+
// would inject the custom stylesheet, and never renders the admin-configured header/footer HTML - this
|
|
59
|
+
// console handles escrow key material, so no admin-supplied markup or CSS runs on it.
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
let cancelled = false;
|
|
62
|
+
getBranding()
|
|
63
|
+
.then((branding) => {
|
|
64
|
+
if (!cancelled) setIconSrc(branding.iconUrl || branding.logoUrl || DEFAULT_ICON_SRC);
|
|
65
|
+
})
|
|
66
|
+
.catch(() => {
|
|
67
|
+
// Decorative only - keep the default icon.
|
|
68
|
+
});
|
|
69
|
+
return () => {
|
|
70
|
+
cancelled = true;
|
|
71
|
+
};
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (!userUid) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// limit=1 - this call exists only to confirm the API is reachable for this signed-in caller, not
|
|
79
|
+
// to read any real data (every page below fetches its own data itself).
|
|
80
|
+
listMatters({ limit: 1 })
|
|
81
|
+
.then(() => setStatus("authorized"))
|
|
82
|
+
.catch((err) => {
|
|
83
|
+
setError(err instanceof ApiRequestError ? err.message : "Could not verify escrow console access.");
|
|
84
|
+
setStatus("error");
|
|
85
|
+
});
|
|
86
|
+
}, [userUid]);
|
|
87
|
+
|
|
88
|
+
function handleSignOut() {
|
|
89
|
+
// Ends the auth-server session and tells other tabs, not just navigates - see `signOutOfConsole()`.
|
|
90
|
+
void signOutOfConsole(authServerUrl);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let content: ReactNode;
|
|
94
|
+
if (!userUid || status === "checking") {
|
|
95
|
+
content = <div className="min-h-screen" />;
|
|
96
|
+
} else if (status === "error") {
|
|
97
|
+
content = (
|
|
98
|
+
<div className="min-h-screen flex items-center justify-center p-8">
|
|
99
|
+
<div className="w-full max-w-md">
|
|
100
|
+
<Alert>{error}</Alert>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
} else {
|
|
105
|
+
const activeItem = NAV_ITEMS.find((item) => item.id === active);
|
|
106
|
+
content = (
|
|
107
|
+
<div className="min-h-screen flex flex-col bg-surface-alt">
|
|
108
|
+
<div className="flex-1 flex min-h-0">
|
|
109
|
+
<nav
|
|
110
|
+
aria-label="Escrow sections"
|
|
111
|
+
className="hidden md:flex w-16 shrink-0 bg-surface border-r border-border flex-col items-center py-3 gap-1"
|
|
112
|
+
>
|
|
113
|
+
<img src={iconSrc} width="96" height="96" alt="" className="mb-3" />
|
|
114
|
+
{NAV_ITEMS.map(({ id, href, label, icon: Icon }) => (
|
|
115
|
+
<a
|
|
116
|
+
key={id}
|
|
117
|
+
href={href}
|
|
118
|
+
aria-label={label}
|
|
119
|
+
aria-current={id === active ? "page" : undefined}
|
|
120
|
+
title={label}
|
|
121
|
+
className={[
|
|
122
|
+
"w-10 h-10 flex items-center justify-center rounded-sm",
|
|
123
|
+
id === active
|
|
124
|
+
? "bg-primary/10 text-primary-dark"
|
|
125
|
+
: "text-text-muted hover:bg-surface-alt hover:text-text",
|
|
126
|
+
].join(" ")}
|
|
127
|
+
>
|
|
128
|
+
<Icon size={20} aria-hidden="true" />
|
|
129
|
+
</a>
|
|
130
|
+
))}
|
|
131
|
+
</nav>
|
|
132
|
+
<BottomTabBar apps={NAV_ITEMS} active={active} />
|
|
133
|
+
<div className="flex-1 flex flex-col min-w-0">
|
|
134
|
+
<header className="h-16 shrink-0 bg-surface border-b border-border flex items-center justify-between gap-4 px-6">
|
|
135
|
+
<span className="font-display font-bold text-lg uppercase tracking-wide">{activeItem?.label}</span>
|
|
136
|
+
<UserMenu userUid={userUid} authServerUrl={authServerUrl} onSignOut={handleSignOut} />
|
|
137
|
+
</header>
|
|
138
|
+
<div className="flex-1 pb-14 md:pb-0">
|
|
139
|
+
<main className="max-w-6xl mx-auto px-6 py-8">{children}</main>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return <>{content}</>;
|
|
148
|
+
}
|
|
@@ -3,30 +3,66 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import "../../styles/app.css";
|
|
6
|
-
import React, { PropsWithChildren,
|
|
6
|
+
import React, { PropsWithChildren, useEffect, useRef, useState } from "react";
|
|
7
7
|
import type { IconType } from "react-icons";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
HiOutlineCalendarDays,
|
|
10
|
+
HiOutlineClipboardDocumentList,
|
|
11
|
+
HiOutlineEnvelope,
|
|
12
|
+
HiOutlinePuzzlePiece,
|
|
13
|
+
HiOutlineUsers,
|
|
14
|
+
} from "react-icons/hi2";
|
|
9
15
|
import { useRedirectIfUnauthenticated } from "@rapidmx/react-shared/auth/session.js";
|
|
10
16
|
import { getSetupStatus } from "@rapidmx/react-shared/admin/setupApi.js";
|
|
11
17
|
import { stopImpersonating } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
12
18
|
import useBranding from "@rapidmx/react-shared/branding/useBranding.js";
|
|
13
19
|
import { useIdleKeyTimeout } from "@rapidmx/react-shared/crypto/useIdleKeyTimeout.js";
|
|
14
20
|
import ComposeProvider from "../mail/compose/ComposeContext.js";
|
|
15
|
-
import
|
|
21
|
+
import { flushComposeDrafts, markSigningOut } from "../mail/compose/composeFlushRegistry.js";
|
|
22
|
+
import BottomTabBar, { NavItem } from "@rapidmx/react-shared/components/navigation/BottomTabBar.js";
|
|
16
23
|
import { BrandingFooter, BrandingHeader } from "./BrandingChrome.js";
|
|
17
24
|
import UserMenu from "./UserMenu.js";
|
|
18
25
|
import { UnlockPromptProvider } from "./UnlockPromptProvider.js";
|
|
19
|
-
import { destroyAllLocalIndexes } from "../../search/localIndexRpcClient.js";
|
|
26
|
+
import { SIGN_OUT_CHANNEL, destroyAllLocalIndexes } from "../../search/localIndexRpcClient.js";
|
|
27
|
+
import { authApiFetch } from "@rapidmx/react-shared/util/api.js";
|
|
28
|
+
import { destroyUnlockedKeys } from "@rapidmx/react-shared/crypto/keySession.js";
|
|
29
|
+
import { clearPinnedSignerCache } from "../mail/pinnedSigners.js";
|
|
30
|
+
import { mergePluginNavItems, PluginNav, PluginNavProps } from "../../plugins/pluginNav.js";
|
|
31
|
+
|
|
32
|
+
/** How long sign-out waits for auth-server's logout before navigating anyway. */
|
|
33
|
+
export const LOGOUT_TIMEOUT_MS = 3_000;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Calls auth-server's logout (`POST /api/auth/logout`, cross-origin with credentials), which clears the auth
|
|
37
|
+
* cookie and invalidates the session's refresh token. Bounded by `LOGOUT_TIMEOUT_MS` and never rejects - a
|
|
38
|
+
* failure (unreachable server, CORS) must not keep the user from leaving.
|
|
39
|
+
*/
|
|
40
|
+
async function logOutOfAuthServer(authServerUrl: string | undefined): Promise<void> {
|
|
41
|
+
if (!authServerUrl) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const timer = setTimeout(() => controller.abort(), LOGOUT_TIMEOUT_MS);
|
|
46
|
+
try {
|
|
47
|
+
await authApiFetch(authServerUrl, "/auth/logout", { method: "POST", signal: controller.signal });
|
|
48
|
+
} catch {
|
|
49
|
+
// Navigate anyway - see this function's doc comment.
|
|
50
|
+
} finally {
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
20
54
|
|
|
21
55
|
export type AppShellApp = "mail" | "calendar" | "contacts" | "tasks";
|
|
22
56
|
|
|
23
57
|
/** `"settings"` is a valid `active` value but deliberately has no entry in `APPS` below — Settings is
|
|
24
58
|
* reached via a `UserMenu` item, not a 5th rail icon (see `SettingsShell.tsx`), so it highlights no
|
|
25
|
-
* rail/tab icon at all; only the header title
|
|
26
|
-
|
|
59
|
+
* rail/tab icon at all; only the header title needs to account for it. Any other string is a plugin's
|
|
60
|
+
* `appRail` item id (see `PluginNav`). */
|
|
61
|
+
export type AppShellActive = AppShellApp | "settings" | (string & {});
|
|
27
62
|
|
|
28
|
-
export interface AppShellProps {
|
|
29
|
-
/** Which icon in the rail is highlighted as the current app — `"settings"` highlights none
|
|
63
|
+
export interface AppShellProps extends PluginNavProps {
|
|
64
|
+
/** Which icon in the rail is highlighted as the current app — `"settings"` highlights none, and a plugin
|
|
65
|
+
* app page passes its own `appRail` item id. */
|
|
30
66
|
active: AppShellActive;
|
|
31
67
|
/** Populated automatically by the framework from an authenticated request (e.g. a valid `jwt` cookie). */
|
|
32
68
|
userUid?: string;
|
|
@@ -58,15 +94,19 @@ export const APPS: AppDef[] = [
|
|
|
58
94
|
{ id: "tasks", href: "/tasks", label: "Tasks", icon: HiOutlineClipboardDocumentList },
|
|
59
95
|
];
|
|
60
96
|
|
|
61
|
-
/**
|
|
62
|
-
*
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
97
|
+
/** Ids plugin `appRail` items can't take besides `APPS`' own - `"settings"` has no rail icon but is still a
|
|
98
|
+
* core `active` value. */
|
|
99
|
+
const RESERVED_APP_IDS = ["settings"];
|
|
100
|
+
|
|
101
|
+
/** `APPS` followed by the plugins' `appRail` items (generic icon), core ids winning - see `mergePluginNavItems`. */
|
|
102
|
+
export function appRailItems(pluginNav?: PluginNav): NavItem[] {
|
|
103
|
+
return mergePluginNavItems<NavItem>(
|
|
104
|
+
APPS,
|
|
105
|
+
pluginNav?.appRail,
|
|
106
|
+
({ id, href, label }) => ({ id, href, label, icon: HiOutlinePuzzlePiece }),
|
|
107
|
+
RESERVED_APP_IDS,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
70
110
|
|
|
71
111
|
/**
|
|
72
112
|
* The persistent chrome shared by every webmail app (Mail, Calendar, Contacts, Tasks): a left icon rail for
|
|
@@ -82,9 +122,11 @@ export default function AppShell({
|
|
|
82
122
|
impersonating,
|
|
83
123
|
impersonationBaseUrl,
|
|
84
124
|
trusted,
|
|
125
|
+
pluginNav,
|
|
85
126
|
children,
|
|
86
127
|
}: PropsWithChildren<AppShellProps>) {
|
|
87
128
|
const [stoppingImpersonation, setStoppingImpersonation] = useState(false);
|
|
129
|
+
const signingOutRef = useRef(false);
|
|
88
130
|
const { branding, iconSrc } = useBranding();
|
|
89
131
|
|
|
90
132
|
useRedirectIfUnauthenticated(userUid, authServerUrl);
|
|
@@ -92,10 +134,11 @@ export default function AppShell({
|
|
|
92
134
|
// specifically so activity in *any* app resets the idle clock - see that hook's own doc comment.
|
|
93
135
|
useIdleKeyTimeout();
|
|
94
136
|
|
|
95
|
-
// An administrator on a server that hasn't finished first-run setup is sent to the setup wizard.
|
|
96
|
-
//
|
|
137
|
+
// An administrator on a server that hasn't finished first-run setup is sent to the setup wizard. The status
|
|
138
|
+
// check is admin-only, so it's only made for a trusted caller - everyone else would just get a 403 on every
|
|
139
|
+
// page load. Any failure is ignored.
|
|
97
140
|
useEffect(() => {
|
|
98
|
-
if (!userUid || impersonating) {
|
|
141
|
+
if (!userUid || impersonating || !trusted) {
|
|
99
142
|
return;
|
|
100
143
|
}
|
|
101
144
|
getSetupStatus()
|
|
@@ -105,15 +148,56 @@ export default function AppShell({
|
|
|
105
148
|
}
|
|
106
149
|
})
|
|
107
150
|
.catch(() => undefined);
|
|
108
|
-
}, [userUid, impersonating]);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
151
|
+
}, [userUid, impersonating, trusted]);
|
|
152
|
+
|
|
153
|
+
// Another tab signing out (this app's `handleSignOut`, or the admin/escrow consoles' `signOutOfConsole()`)
|
|
154
|
+
// ended this session too - its auth cookie is gone - so this tab destroys its own unlocked keys and every
|
|
155
|
+
// local search index on the device, then leaves as well. The consoles have no local-index client of their
|
|
156
|
+
// own, so this is what actually removes the indexes after a console sign-out (the console also records a
|
|
157
|
+
// pending deletion, retried on the next mail load, in case no mail tab is open). `destroyAllLocalIndexes()`
|
|
158
|
+
// announces the sign-out on this same channel, which this tab then hears itself, so the ref is set first:
|
|
159
|
+
// each tab reacts once, and the tab that started the sign-out ignores its own announcement.
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (!userUid || typeof BroadcastChannel === "undefined") {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const channel = new BroadcastChannel(SIGN_OUT_CHANNEL);
|
|
165
|
+
channel.addEventListener("message", (event: MessageEvent<{ type?: string }>) => {
|
|
166
|
+
if (event.data?.type !== "sign-out" || signingOutRef.current) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
signingOutRef.current = true;
|
|
170
|
+
// Compose windows must not ask "Leave site?" - that would let this forced navigation be cancelled.
|
|
171
|
+
markSigningOut();
|
|
172
|
+
destroyUnlockedKeys();
|
|
173
|
+
clearPinnedSignerCache();
|
|
174
|
+
// Bounded by its own timeout and never rejects - awaited so navigating doesn't kill the Worker mid-delete.
|
|
175
|
+
void destroyAllLocalIndexes().then(() => {
|
|
176
|
+
window.location.href = authServerUrl ?? "/";
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
return () => channel.close();
|
|
180
|
+
}, [userUid, authServerUrl]);
|
|
181
|
+
|
|
182
|
+
async function handleSignOut() {
|
|
183
|
+
signingOutRef.current = true;
|
|
184
|
+
// Before flushing and navigating: compose windows then skip their "Leave site?" prompt, which could
|
|
185
|
+
// otherwise cancel the sign-out's own navigation.
|
|
186
|
+
markSigningOut();
|
|
187
|
+
// Unlocked private keys never outlive an explicit sign-out.
|
|
188
|
+
destroyUnlockedKeys();
|
|
189
|
+
// Trusted signer pins read from contacts don't outlive the session either.
|
|
190
|
+
clearPinnedSignerCache();
|
|
191
|
+
// Open compose windows save edits still waiting on their autosave debounce while the session is still
|
|
192
|
+
// valid - logout invalidates it. Bounded the same way as logout itself, and never rejects.
|
|
193
|
+
await flushComposeDrafts(LOGOUT_TIMEOUT_MS);
|
|
194
|
+
// The Tier 2 local index MUST be destroyed on explicit logout, the same as unlocked keys themselves
|
|
195
|
+
// (spec §11). Destroys every index on this device (not only mailboxes opened this page load) and is
|
|
196
|
+
// awaited before navigating - a navigation tears down the Worker mid-delete otherwise.
|
|
197
|
+
// destroyAllLocalIndexes() is bounded by its own timeout and never rejects, so sign-out can't hang.
|
|
198
|
+
// In parallel, auth-server's logout clears the auth cookie and invalidates the session's refresh
|
|
199
|
+
// token - without it, "Sign Out" would only navigate away from a still-valid session.
|
|
200
|
+
await Promise.all([destroyAllLocalIndexes(), logOutOfAuthServer(authServerUrl)]);
|
|
117
201
|
window.location.href = authServerUrl ?? "/";
|
|
118
202
|
}
|
|
119
203
|
|
|
@@ -134,12 +218,18 @@ export default function AppShell({
|
|
|
134
218
|
return <div className="min-h-screen" />;
|
|
135
219
|
}
|
|
136
220
|
|
|
221
|
+
const apps = appRailItems(pluginNav);
|
|
222
|
+
// The header title: "settings" has no rail item, everything else is labelled by its own rail item.
|
|
223
|
+
const activeLabel = active === "settings" ? "Settings" : apps.find((app) => app.id === active)?.label;
|
|
224
|
+
|
|
137
225
|
return (
|
|
138
226
|
// Mounted here, not scoped to Mail/Settings, for the same reason as useIdleKeyTimeout() above -
|
|
139
227
|
// ComposeWindow's sign/encrypt toggles and MessageDetailPane's encrypted-message view (both Mail)
|
|
140
228
|
// are today's only useUnlockPrompt() callers, but this needs to be available to any app shell.
|
|
141
229
|
<UnlockPromptProvider>
|
|
142
|
-
|
|
230
|
+
{/* An impersonating admin acts with the impersonated user's access, so their own trusted role
|
|
231
|
+
mustn't skip the per-mailbox checks. */}
|
|
232
|
+
<ComposeProvider userUid={userUid} trusted={!!trusted && !impersonating}>
|
|
143
233
|
<div className="min-h-screen flex flex-col bg-surface-alt">
|
|
144
234
|
<BrandingHeader branding={branding} />
|
|
145
235
|
{impersonating && (
|
|
@@ -163,7 +253,7 @@ export default function AppShell({
|
|
|
163
253
|
className="hidden md:flex w-16 shrink-0 bg-surface border-r border-border flex-col items-center py-3 gap-1"
|
|
164
254
|
>
|
|
165
255
|
<img src={iconSrc} width="96" height="96" alt="" className="mb-3" />
|
|
166
|
-
{
|
|
256
|
+
{apps.map(({ id, href, label, icon: Icon }) => (
|
|
167
257
|
<a
|
|
168
258
|
key={id}
|
|
169
259
|
href={href}
|
|
@@ -181,10 +271,10 @@ export default function AppShell({
|
|
|
181
271
|
</a>
|
|
182
272
|
))}
|
|
183
273
|
</nav>
|
|
184
|
-
<BottomTabBar apps={
|
|
274
|
+
<BottomTabBar apps={apps} active={active} />
|
|
185
275
|
<div className="flex-1 flex flex-col min-w-0">
|
|
186
276
|
<header className="h-16 shrink-0 bg-surface border-b border-border flex items-center justify-between gap-4 px-6">
|
|
187
|
-
<span className="font-display font-bold text-lg uppercase tracking-wide">{
|
|
277
|
+
<span className="font-display font-bold text-lg uppercase tracking-wide">{activeLabel}</span>
|
|
188
278
|
<UserMenu userUid={userUid} authServerUrl={authServerUrl} onSignOut={handleSignOut} showAdminLink={trusted} showSettingsLink />
|
|
189
279
|
</header>
|
|
190
280
|
<div className="flex-1 flex min-h-0 pb-14 md:pb-0">{children}</div>
|
|
@@ -3,23 +3,61 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
///////////////////////////////////////////////////////////////////////////////
|
|
5
5
|
import React from "react";
|
|
6
|
+
import DOMPurify from "dompurify";
|
|
6
7
|
import { Branding } from "@rapidmx/react-shared/branding/brandingApi.js";
|
|
7
8
|
|
|
9
|
+
/** Elements that could run script, restyle the whole page, collect input, or embed another page - stripped on
|
|
10
|
+
* top of DOMPurify's defaults (which already drop `<script>`, `on*` handlers, and `javascript:` URLs). */
|
|
11
|
+
const FORBIDDEN_TAGS = [
|
|
12
|
+
"script",
|
|
13
|
+
"style",
|
|
14
|
+
"form",
|
|
15
|
+
"input",
|
|
16
|
+
"button",
|
|
17
|
+
"textarea",
|
|
18
|
+
"select",
|
|
19
|
+
"option",
|
|
20
|
+
"iframe",
|
|
21
|
+
"frame",
|
|
22
|
+
"frameset",
|
|
23
|
+
"object",
|
|
24
|
+
"embed",
|
|
25
|
+
"link",
|
|
26
|
+
"meta",
|
|
27
|
+
"base",
|
|
28
|
+
"svg",
|
|
29
|
+
"math",
|
|
30
|
+
];
|
|
31
|
+
const FORBIDDEN_ATTRS = ["action", "formaction", "srcdoc"];
|
|
32
|
+
|
|
8
33
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
34
|
+
* Sanitizes admin-configured branding HTML before it's rendered. The server sanitizes on save too; this also
|
|
35
|
+
* covers anything stored before that. Returns `""` where there's no DOM to sanitize with (server-side
|
|
36
|
+
* rendering), so unsanitized HTML is never emitted.
|
|
12
37
|
*/
|
|
13
|
-
export function
|
|
14
|
-
if (!
|
|
15
|
-
return
|
|
38
|
+
export function sanitizeBrandingHtml(html: string): string {
|
|
39
|
+
if (!DOMPurify.isSupported) {
|
|
40
|
+
return "";
|
|
16
41
|
}
|
|
17
|
-
return
|
|
42
|
+
return DOMPurify.sanitize(html, { FORBID_TAGS: FORBIDDEN_TAGS, FORBID_ATTR: FORBIDDEN_ATTRS });
|
|
18
43
|
}
|
|
19
44
|
|
|
20
|
-
|
|
21
|
-
|
|
45
|
+
function BrandingHtml({ html }: { html: string | undefined }) {
|
|
46
|
+
const clean = html ? sanitizeBrandingHtml(html) : "";
|
|
47
|
+
if (!clean) {
|
|
22
48
|
return null;
|
|
23
49
|
}
|
|
24
|
-
return <div dangerouslySetInnerHTML={{ __html:
|
|
50
|
+
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Renders the admin-configured `Branding.headerHtml`/`footerHtml` (see `useBranding()`), sanitized - shared by
|
|
55
|
+
* the webmail and admin chromes. The escrow console deliberately renders no branding HTML at all.
|
|
56
|
+
*/
|
|
57
|
+
export function BrandingHeader({ branding }: { branding: Branding | null }) {
|
|
58
|
+
return <BrandingHtml html={branding?.headerHtml} />;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function BrandingFooter({ branding }: { branding: Branding | null }) {
|
|
62
|
+
return <BrandingHtml html={branding?.footerHtml} />;
|
|
25
63
|
}
|