@rapidmx/web-client 0.5.0 → 0.7.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/shared/components/admin/layout/AdminShell.tsx +25 -8
- package/apps/shared/components/admin/settings/BrandingForm.tsx +398 -385
- package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +119 -112
- package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +159 -152
- package/apps/shared/components/admin/settings/PluginsManager.tsx +1288 -1235
- package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +9 -1
- package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +288 -290
- package/apps/shared/components/admin/setup/SetupWizard.tsx +443 -416
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +194 -192
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +199 -197
- package/apps/shared/components/layout/AppShell.tsx +288 -270
- package/apps/shared/components/mail/ConversationList.tsx +225 -33
- package/apps/shared/components/mail/ConversationThreadPane.tsx +434 -216
- package/apps/shared/components/mail/MailListToolbar.tsx +265 -0
- package/apps/shared/components/mail/MailSelectionBar.tsx +222 -0
- package/apps/shared/components/mail/MenuButton.tsx +384 -0
- package/apps/shared/components/mail/MessageDetailPane.tsx +280 -215
- package/apps/shared/components/mail/MoveToFolderDialog.tsx +263 -0
- package/apps/shared/components/mail/compose/ComposeContext.tsx +29 -2
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +1700 -1652
- package/apps/shared/components/mail/compose/RecipientInput.tsx +356 -0
- package/apps/shared/components/mail/compose/RichTextEditor.tsx +38 -3
- package/apps/shared/components/mail/compose/quotedBody.ts +100 -0
- package/apps/shared/components/mail/compose/recipients.ts +104 -0
- package/apps/shared/components/mail/labelMenu.tsx +278 -0
- package/apps/shared/components/mail/layout/MailShell.tsx +31 -4
- package/apps/shared/components/mail/listPreferences.ts +228 -0
- package/apps/shared/components/settings/layout/SettingsShell.tsx +23 -10
- package/apps/shared/components/tasks/layout/TasksShell.tsx +201 -199
- package/apps/shared/plugins/pluginNav.ts +73 -0
- package/apps/shared/search/localIndexRpcClient.ts +3 -1
- package/apps/shared/styles/app.css +28 -5
- package/apps/www/index.tsx +1854 -1314
- package/apps/www/messages/[uid].tsx +5 -4
- 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/branding/index.d.ts +3 -0
- package/dist/apps/admin/data-requests/index.d.ts +3 -0
- package/dist/apps/admin/distribution-lists/[uid].d.ts +7 -0
- 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/index.d.ts +3 -0
- package/dist/apps/admin/escrow-scopes/new/index.d.ts +3 -0
- package/dist/apps/admin/index.d.ts +3 -0
- package/dist/apps/admin/ingest-queue/index.d.ts +4 -0
- 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/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/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/_transportRuleConfig.d.ts +21 -0
- package/dist/apps/admin/transport-rules/index.d.ts +3 -0
- package/dist/apps/admin/transport-rules/new/index.d.ts +3 -0
- package/dist/apps/escrow/_layout.d.ts +10 -0
- package/dist/apps/escrow/audit-log/index.d.ts +9 -0
- package/dist/apps/escrow/index.d.ts +3 -0
- package/dist/apps/escrow/matters/[uid].d.ts +7 -0
- 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/layout/AdminShell.d.ts +32 -0
- package/dist/apps/shared/components/admin/layout/AdminShell.js +12 -5
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +12 -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/ShareAccessCard.d.ts +12 -0
- package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +13 -0
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +6 -5
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +12 -0
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +15 -0
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +2 -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/MailboxPolicyForm.d.ts +13 -0
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +2 -2
- package/dist/apps/shared/components/admin/settings/PluginsManager.d.ts +11 -0
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +33 -14
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +12 -0
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +2 -2
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.d.ts +14 -0
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +1 -1
- package/dist/apps/shared/components/admin/setup/SetupWizard.d.ts +36 -0
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +14 -10
- package/dist/apps/shared/components/admin/signOut.d.ts +23 -0
- package/dist/apps/shared/components/admin/usePagedList.d.ts +39 -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/MonthView.d.ts +15 -0
- package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +20 -0
- 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/TimeGridView.d.ts +17 -0
- package/dist/apps/shared/components/calendar/allDay.d.ts +51 -0
- package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +40 -0
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +2 -2
- package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +23 -0
- package/dist/apps/shared/components/contacts/ContactForm.d.ts +20 -0
- package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +40 -0
- 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/contactKeys.d.ts +29 -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/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 +15 -13
- package/dist/apps/shared/components/layout/BrandingChrome.d.ts +18 -0
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +51 -0
- package/dist/apps/shared/components/layout/MailboxProvisioning.d.ts +13 -0
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +66 -0
- package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +45 -0
- package/dist/apps/shared/components/layout/UserMenu.d.ts +23 -0
- package/dist/apps/shared/components/mail/ConversationList.d.ts +47 -0
- package/dist/apps/shared/components/mail/ConversationList.js +84 -10
- package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +62 -0
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +217 -60
- package/dist/apps/shared/components/mail/MailListToolbar.d.ts +55 -0
- package/dist/apps/shared/components/mail/MailListToolbar.js +149 -0
- package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +59 -0
- package/dist/apps/shared/components/mail/MailSelectionBar.js +48 -0
- package/dist/apps/shared/components/mail/MenuButton.d.ts +71 -0
- package/dist/apps/shared/components/mail/MenuButton.js +211 -0
- package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +101 -0
- package/dist/apps/shared/components/mail/MessageDetailPane.js +126 -129
- package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +57 -0
- package/dist/apps/shared/components/mail/MoveToFolderDialog.js +138 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +73 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +3 -1
- 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 +52 -20
- 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/RecipientInput.d.ts +36 -0
- package/dist/apps/shared/components/mail/compose/RecipientInput.js +203 -0
- package/dist/apps/shared/components/mail/compose/RichTextEditor.d.ts +43 -0
- package/dist/apps/shared/components/mail/compose/RichTextEditor.js +21 -3
- 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/quotedBody.d.ts +37 -0
- package/dist/apps/shared/components/mail/compose/quotedBody.js +85 -0
- package/dist/apps/shared/components/mail/compose/recipients.d.ts +24 -0
- package/dist/apps/shared/components/mail/compose/recipients.js +87 -0
- package/dist/apps/shared/components/mail/labelMenu.d.ts +97 -0
- package/dist/apps/shared/components/mail/labelMenu.js +134 -0
- package/dist/apps/shared/components/mail/layout/MailShell.d.ts +65 -0
- package/dist/apps/shared/components/mail/layout/MailShell.js +17 -5
- package/dist/apps/shared/components/mail/listPreferences.d.ts +101 -0
- package/dist/apps/shared/components/mail/listPreferences.js +186 -0
- package/dist/apps/shared/components/mail/pinnedSigners.d.ts +33 -0
- package/dist/apps/shared/components/mail/verificationSeals.d.ts +22 -0
- package/dist/apps/shared/components/mail/writableMailboxes.d.ts +15 -0
- package/dist/apps/shared/components/rules/RuleBuilder.d.ts +70 -0
- package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +41 -0
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +19 -10
- 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/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/localIndexBlockCipher.d.ts +36 -0
- package/dist/apps/shared/search/localIndexBuilder.d.ts +85 -0
- 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 +3 -1
- package/dist/apps/shared/search/localIndexSchema.d.ts +111 -0
- 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/localIndexVFS.d.ts +101 -0
- package/dist/apps/shared/search/localIndexWorker.d.ts +145 -0
- package/dist/apps/shared/search/searchTier2.d.ts +30 -0
- package/dist/apps/shared/styles/app.css +28 -5
- 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/contacts/[uid].d.ts +13 -0
- package/dist/apps/www/contacts/index.d.ts +3 -0
- package/dist/apps/www/index.d.ts +3 -0
- package/dist/apps/www/index.js +506 -112
- package/dist/apps/www/messages/[uid].d.ts +11 -0
- package/dist/apps/www/messages/[uid].js +2 -3
- package/dist/apps/www/settings/auto-reply/index.d.ts +4 -0
- package/dist/apps/www/settings/encryption/index.d.ts +7 -0
- package/dist/apps/www/settings/filters/[uid].d.ts +8 -0
- 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/labels/index.d.ts +4 -0
- package/dist/apps/www/settings/privacy/index.d.ts +4 -0
- package/dist/apps/www/settings/read-receipts/index.d.ts +4 -0
- package/dist/apps/www/settings/sharing/index.d.ts +4 -0
- 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/package.json +3 -3
- package/apps/shared/components/booking/AvailabilityEditor.tsx +0 -118
- package/apps/www/settings/booking-types/[uid].tsx +0 -306
- package/apps/www/settings/booking-types/index.tsx +0 -98
- package/apps/www/settings/booking-types/new/index.tsx +0 -212
- package/apps/www/settings/focused-inbox/index.tsx +0 -150
- package/dist/apps/shared/components/booking/AvailabilityEditor.js +0 -45
- package/dist/apps/www/settings/booking-types/[uid].js +0 -134
- package/dist/apps/www/settings/booking-types/index.js +0 -29
- package/dist/apps/www/settings/booking-types/new/index.js +0 -86
- package/dist/apps/www/settings/focused-inbox/index.js +0 -71
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
3
|
+
import { CalendarOccurrence } from "@rapidmx/react-shared/calendar/recurrence.js";
|
|
4
|
+
export interface EventModalProps {
|
|
5
|
+
open: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
mailboxUid: string;
|
|
8
|
+
/** The calendar folder a new event is created into (ignored when editing — an existing event keeps
|
|
9
|
+
* its own `folderUid`). When `calendars` names more than one option, a "Calendar" selector lets the
|
|
10
|
+
* user override this default before saving. */
|
|
11
|
+
folderUid: string;
|
|
12
|
+
/** Every calendar the caller could create this event into. Omitted, or a single entry, means "only
|
|
13
|
+
* one calendar exists" — no selector is shown and `folderUid` is used as-is, matching this
|
|
14
|
+
* component's original single-calendar behavior exactly. */
|
|
15
|
+
calendars?: {
|
|
16
|
+
uid: string;
|
|
17
|
+
name: string;
|
|
18
|
+
}[];
|
|
19
|
+
/** Every mailbox a new event could be created in, each with its own calendars. With more than one, a
|
|
20
|
+
* "Mailbox" selector (create mode only) chooses the mailbox, which in turn drives the Calendar selector
|
|
21
|
+
* and the organizer address; `mailboxUid`/`folderUid` are the initial selection. */
|
|
22
|
+
mailboxOptions?: {
|
|
23
|
+
mailbox: Mailbox;
|
|
24
|
+
calendars: {
|
|
25
|
+
uid: string;
|
|
26
|
+
name: string;
|
|
27
|
+
}[];
|
|
28
|
+
}[];
|
|
29
|
+
organizerAddress: string;
|
|
30
|
+
/** The viewing mailbox's alias addresses - an event it organizes (or an invitation to it) may name any of
|
|
31
|
+
* them instead of `organizerAddress`. Defaults to that mailbox's `aliasAddresses` from `mailboxOptions`. */
|
|
32
|
+
organizerAliases?: string[];
|
|
33
|
+
/** `null` when creating a new event. */
|
|
34
|
+
occurrence: CalendarOccurrence | null;
|
|
35
|
+
/** Prefilled start/end for create mode (e.g. the day/slot the user clicked). */
|
|
36
|
+
initialStart?: Date;
|
|
37
|
+
initialEnd?: Date;
|
|
38
|
+
onSaved: () => void;
|
|
39
|
+
onDeleted: () => void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create/view/edit/delete for a single calendar event. Editing or deleting a recurring event's
|
|
43
|
+
* occurrence offers a choice between "this event" and "the entire series" (see
|
|
44
|
+
* `calendarMutations.ts`) — Outlook's own convention for the same ambiguity.
|
|
45
|
+
*/
|
|
46
|
+
export default function EventModal({ open, onClose, mailboxUid, folderUid, calendars, mailboxOptions, organizerAddress, organizerAliases, occurrence, initialStart, initialEnd, onSaved, onDeleted, }: EventModalProps): React.JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { CalendarOccurrence } from "@rapidmx/react-shared/calendar/recurrence.js";
|
|
3
|
+
export interface MonthViewProps {
|
|
4
|
+
/** Any date within the month to display. */
|
|
5
|
+
viewDate: Date;
|
|
6
|
+
/** Already expanded for the visible grid range — see `recurrence.ts`'s `expandAllOccurrences`. */
|
|
7
|
+
occurrences: CalendarOccurrence[];
|
|
8
|
+
/** Each occurrence's own calendar color, keyed by `folderUid` — see `calendarColors.ts`. */
|
|
9
|
+
folderColors: Record<string, string>;
|
|
10
|
+
onSelectDay: (date: Date) => void;
|
|
11
|
+
onSelectEvent: (occurrence: CalendarOccurrence) => void;
|
|
12
|
+
}
|
|
13
|
+
/** A real 6-week month grid (Mon-start), matching Outlook/Gmail's month view. Multi-day and all-day
|
|
14
|
+
* events are shown on every day they cover (see `allDay.ts`'s `occursOnDay`). */
|
|
15
|
+
export default function MonthView({ viewDate, occurrences, folderColors, onSelectDay, onSelectEvent }: MonthViewProps): React.JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { RecurrenceRule, WeekdayCode } from "@rapidmx/react-shared/calendar/calendarApi.js";
|
|
3
|
+
export interface RecurrenceEditorProps {
|
|
4
|
+
/** `null` means "does not repeat". */
|
|
5
|
+
value: RecurrenceRule | null;
|
|
6
|
+
onChange: (value: RecurrenceRule | null) => void;
|
|
7
|
+
/** Whether the event is all-day - its series expands in UTC, so the inclusive "Ends on" date is stored as
|
|
8
|
+
* the end of that UTC day rather than the local one (see `allDay.ts`'s `recurrenceUntilInstant()`). */
|
|
9
|
+
allDay?: boolean;
|
|
10
|
+
/** The weekday the event starts on (in the frame its series expands in - see `allDay.ts`'s
|
|
11
|
+
* `startWeekdayCode()`): the day a new weekly rule, or one switched to Weekly, repeats on. Defaults to Monday. */
|
|
12
|
+
startWeekday?: WeekdayCode;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A recurring-event editor: frequency + interval, a weekday picker (weekly only), and an end
|
|
16
|
+
* condition (never / after N occurrences / on a date) — everything `RecurrenceRule` can express. The
|
|
17
|
+
* live "every ... until/for ..." summary comes from `describeRecurrence()` (built on `rrule`'s own
|
|
18
|
+
* `.toText()`), so it never drifts out of sync with what will actually be submitted.
|
|
19
|
+
*/
|
|
20
|
+
export default function RecurrenceEditor({ value, onChange, allDay, startWeekday }: RecurrenceEditorProps): React.JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React, { RefObject } from "react";
|
|
2
|
+
import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
3
|
+
export interface ResourcePickerProps {
|
|
4
|
+
anchorRef: RefObject<HTMLElement | null>;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
onSelect: (mailbox: Mailbox) => void;
|
|
7
|
+
/** Addresses already added as attendees (lowercased by the caller) — excluded from the list so the
|
|
8
|
+
* same room/equipment mailbox can't be picked twice. */
|
|
9
|
+
excludeAddresses: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A searchable dropdown of bookable resource mailboxes (rooms/equipment), for adding one as a `"resource"`
|
|
13
|
+
* attendee in `EventModal` without typing its raw address. Fetches the caller's full visible resource-mailbox
|
|
14
|
+
* list once on open and filters it client-side by name/address as the reader types — the same
|
|
15
|
+
* "fetch-flat-list, filter-client-side" contract every other list endpoint in this codebase already uses
|
|
16
|
+
* (see `calendarApi.ts`'s `listCalendarEvents` doc comment), since there is no dedicated server-side search
|
|
17
|
+
* endpoint for mailboxes and the resource-mailbox count in any one org is expected to be small.
|
|
18
|
+
*/
|
|
19
|
+
export default function ResourcePicker({ anchorRef, onClose, onSelect, excludeAddresses }: ResourcePickerProps): React.JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { CalendarOccurrence } from "@rapidmx/react-shared/calendar/recurrence.js";
|
|
3
|
+
export interface SplitDayColumn {
|
|
4
|
+
folderUid: string;
|
|
5
|
+
name: string;
|
|
6
|
+
color: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SplitDayViewProps {
|
|
9
|
+
day: Date;
|
|
10
|
+
columns: SplitDayColumn[];
|
|
11
|
+
/** Already expanded for `day` — see `recurrence.ts`'s `expandAllOccurrences`. Not pre-filtered by
|
|
12
|
+
* calendar; this component splits them into columns itself by `occurrence.folderUid`. */
|
|
13
|
+
occurrences: CalendarOccurrence[];
|
|
14
|
+
onSelectEvent: (occurrence: CalendarOccurrence) => void;
|
|
15
|
+
/** Click on an empty slot in one calendar's column — the calendar it belongs to is `folderUid`. */
|
|
16
|
+
onSelectSlot: (start: Date, end: Date, folderUid: string) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Outlook's "Split" view: one column per checked calendar, all showing the same single day, so
|
|
20
|
+
* events across calendars can be compared side by side. A deliberately separate component from
|
|
21
|
+
* `TimeGridView` rather than a generalization of it — it shares that component's visual language
|
|
22
|
+
* (same hour rail, same slot height) but is genuinely simpler in one respect: no drag-to-move/resize.
|
|
23
|
+
* Dragging an event between calendar columns would mean reassigning its `folderUid`, a real feature
|
|
24
|
+
* `moveOccurrence`/`resolveDragAction` don't support today; rather than bolt that on, this view is
|
|
25
|
+
* click-only (select an event, or click a slot to create one already targeted at that column's
|
|
26
|
+
* calendar) — a practical scope cut, not an oversight.
|
|
27
|
+
*/
|
|
28
|
+
export default function SplitDayView({ day, columns, occurrences, onSelectEvent, onSelectSlot }: SplitDayViewProps): React.JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { CalendarOccurrence } from "@rapidmx/react-shared/calendar/recurrence.js";
|
|
3
|
+
export interface TimeGridViewProps {
|
|
4
|
+
/** One day for the Day view, seven for the Week view, five for the Work Week view. */
|
|
5
|
+
days: Date[];
|
|
6
|
+
/** Already expanded for the visible range — see `recurrence.ts`'s `expandAllOccurrences`. */
|
|
7
|
+
occurrences: CalendarOccurrence[];
|
|
8
|
+
/** Each occurrence's own calendar color, keyed by `folderUid` — see `calendarColors.ts`. */
|
|
9
|
+
folderColors: Record<string, string>;
|
|
10
|
+
onSelectEvent: (occurrence: CalendarOccurrence) => void;
|
|
11
|
+
/** Click on an empty slot — start/end default to a 30-minute block there, for the "New event" flow. */
|
|
12
|
+
onSelectSlot: (start: Date, end: Date) => void;
|
|
13
|
+
}
|
|
14
|
+
/** An hourly time grid (Week, Work Week, or Day view — they share all their rendering/drag logic and
|
|
15
|
+
* differ only in how many day columns are shown), with a day-header row and all-day events in a
|
|
16
|
+
* header strip above it. */
|
|
17
|
+
export default function TimeGridView({ days, occurrences, folderColors, onSelectEvent, onSelectSlot }: TimeGridViewProps): React.JSX.Element;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { WeekdayCode } from "@rapidmx/react-shared/calendar/calendarApi.js";
|
|
2
|
+
import { CalendarOccurrence } from "@rapidmx/react-shared/calendar/recurrence.js";
|
|
3
|
+
/**
|
|
4
|
+
* All-day events are stored date-only: `startDate` is UTC midnight of the first day and `endDate` is UTC
|
|
5
|
+
* midnight of the day *after* the last day (exclusive, like iCalendar's `DTEND;VALUE=DATE`). That keeps
|
|
6
|
+
* an all-day event on the same calendar date for every viewer, whatever their timezone.
|
|
7
|
+
*
|
|
8
|
+
* Older events were saved as the creator's *local* midnight converted to UTC (e.g. `...T22:00Z` for a
|
|
9
|
+
* UTC+2 creator), so reading one rounds the instant to the nearest UTC midnight rather than slicing the
|
|
10
|
+
* ISO string — which gives the intended date for both formats.
|
|
11
|
+
*/
|
|
12
|
+
export declare function allDayDateKey(iso: string): string;
|
|
13
|
+
/** `YYYY-MM-DD` -> the stored UTC-midnight instant for that date. */
|
|
14
|
+
export declare function allDayInstant(dateKey: string): string;
|
|
15
|
+
/** Adds `days` to a `YYYY-MM-DD` date key. */
|
|
16
|
+
export declare function addDaysToKey(dateKey: string, days: number): string;
|
|
17
|
+
/** The local calendar date `day` (a local-midnight `Date` from a grid view) as a `YYYY-MM-DD` key. */
|
|
18
|
+
export declare function localDateKey(day: Date): string;
|
|
19
|
+
/**
|
|
20
|
+
* A recurrence rule's inclusive "Ends on" date -> its stored `until` instant: the end of that day in the
|
|
21
|
+
* frame the series expands in. A timed series expands in its own (normally the viewer's) timezone, so that's
|
|
22
|
+
* the end of the local day - `new Date("YYYY-MM-DD")` would be UTC midnight, the previous day west of UTC.
|
|
23
|
+
* An all-day series expands in UTC (react-shared's `expandOccurrences`), so it's the end of the UTC day: the
|
|
24
|
+
* local end of day is already the next UTC day west of UTC and would add one more occurrence.
|
|
25
|
+
*/
|
|
26
|
+
export declare function recurrenceUntilInstant(dateKey: string, allDay: boolean): string;
|
|
27
|
+
/**
|
|
28
|
+
* Inverse of `recurrenceUntilInstant()`. An all-day series' value is recognized explicitly in its stored
|
|
29
|
+
* `T23:59:59.999Z` form (and as a bare UTC midnight, the oldest form). Anything else is a legacy value stored
|
|
30
|
+
* as the creator's *local* end of day, which can land anywhere from just before 10:00Z on the chosen date (UTC+14)
|
|
31
|
+
* to just before 12:00Z the day after (UTC-12) - a 26-hour spread no UTC-only rounding can resolve (UTC+14 and UTC-10
|
|
32
|
+
* creators' values are exactly 24h apart for the same date), so it reads back on the local calendar, the
|
|
33
|
+
* frame it was written in.
|
|
34
|
+
*/
|
|
35
|
+
export declare function recurrenceUntilDateKey(until: string, allDay: boolean): string;
|
|
36
|
+
/** The weekday an event starts on, in the frame its series expands in: the UTC date for an all-day event
|
|
37
|
+
* (`start`'s date part), the event's own timezone otherwise (`start` being a local `datetime-local` value).
|
|
38
|
+
* `undefined` while the start input is empty/invalid. */
|
|
39
|
+
export declare function startWeekdayCode(start: string, allDay: boolean, timezone: string | undefined): WeekdayCode | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Whether `occurrence` should be shown on the grid's local calendar day `day`. All-day events cover every
|
|
42
|
+
* date from their start date up to (not including) their exclusive end date — at least their start date.
|
|
43
|
+
* Timed events cover every local day their [start, end) interval overlaps, so a multi-day event shows on
|
|
44
|
+
* each day rather than only on its start day.
|
|
45
|
+
*/
|
|
46
|
+
export declare function occursOnDay(occurrence: CalendarOccurrence, day: Date): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Whether `day` is the first day `occurrence` covers. Grid views only make that day's chip/block
|
|
49
|
+
* draggable — a drag id must be unique, so the continuation copies on later days are display-only.
|
|
50
|
+
*/
|
|
51
|
+
export declare function startsOnDay(occurrence: CalendarOccurrence, day: Date): boolean;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, { PropsWithChildren } from "react";
|
|
2
|
+
import { Folder, Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
3
|
+
import { AppShellProps } from "../../layout/AppShell.js";
|
|
4
|
+
export type CalendarShellProps = Omit<AppShellProps, "active">;
|
|
5
|
+
/** One mailbox's own `calendar`-type folders. `error` is set (and `calendarFolders` left empty) when that
|
|
6
|
+
* mailbox's own `listFolders()` failed - one mailbox's failure never hides every other mailbox's calendars. */
|
|
7
|
+
export interface MailboxCalendars {
|
|
8
|
+
mailbox: Mailbox;
|
|
9
|
+
calendarFolders: Folder[];
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface CalendarShellContextValue {
|
|
13
|
+
/** The default mailbox for "+ New event" (`?mailboxUid=` if accessible, else the caller's own mailbox,
|
|
14
|
+
* else the first accessible one) - every accessible mailbox's calendars are shown regardless. */
|
|
15
|
+
mailboxUid?: string;
|
|
16
|
+
/** `mailboxUid`'s first `calendar`-type folder - the default target for "+ New event". */
|
|
17
|
+
folderUid?: string;
|
|
18
|
+
/** Every accessible mailbox's `calendar`-type folders, flattened - the set of calendars that can be
|
|
19
|
+
* checked/shown (see `CalendarListSidebar`). Each folder's own `mailboxUid` says which mailbox it's in. */
|
|
20
|
+
calendarFolders: Folder[];
|
|
21
|
+
/** The same folders, grouped per mailbox, for the sidebar's per-mailbox sections. */
|
|
22
|
+
mailboxCalendars: MailboxCalendars[];
|
|
23
|
+
mailboxes: Mailbox[];
|
|
24
|
+
/** Re-fetches every mailbox's folders (e.g. after creating a new calendar) without a full reload. */
|
|
25
|
+
reloadFolders: () => void;
|
|
26
|
+
/** The display color for a calendar folder: its own `color`, else the default for the caller's own
|
|
27
|
+
* mailbox, else its mailbox's `accentColorForMailbox()` - so a shared mailbox's calendar is visually
|
|
28
|
+
* distinct from the caller's own even when neither has a color set. */
|
|
29
|
+
colorFor: (folder: Folder) => string;
|
|
30
|
+
}
|
|
31
|
+
/** Reads the calendars the Calendar is currently showing, as resolved by the enclosing `CalendarShell`. */
|
|
32
|
+
export declare function useCalendarShell(): CalendarShellContextValue;
|
|
33
|
+
/**
|
|
34
|
+
* The Calendar app's shell. Fetches every accessible mailbox's calendar folders in parallel so shared
|
|
35
|
+
* mailboxes' calendars show alongside the caller's own, each mailbox color-coded (see `colorFor`) - there
|
|
36
|
+
* is no mailbox switcher. The actual view being looked at (month/week/day, and which one) is *not* shell
|
|
37
|
+
* state — it lives in `apps/www/calendar/index.tsx`'s own local state, since navigating between
|
|
38
|
+
* views/dates must be instant (no full page reload) and this framework has no client-side router.
|
|
39
|
+
*/
|
|
40
|
+
export default function CalendarShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, pluginNav, children, }: PropsWithChildren<CalendarShellProps>): React.JSX.Element;
|
|
@@ -29,7 +29,7 @@ export function useCalendarShell() {
|
|
|
29
29
|
* state — it lives in `apps/www/calendar/index.tsx`'s own local state, since navigating between
|
|
30
30
|
* views/dates must be instant (no full page reload) and this framework has no client-side router.
|
|
31
31
|
*/
|
|
32
|
-
export default function CalendarShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, children, }) {
|
|
32
|
+
export default function CalendarShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, pluginNav, children, }) {
|
|
33
33
|
const [status, setStatus] = useState("checking");
|
|
34
34
|
const [error, setError] = useState(null);
|
|
35
35
|
const [mailboxes, setMailboxes] = useState([]);
|
|
@@ -100,5 +100,5 @@ export default function CalendarShell({ userUid, authServerUrl, impersonating, i
|
|
|
100
100
|
else if (userUid && status === "ready") {
|
|
101
101
|
inner = (_jsx("div", { className: "flex-1 min-w-0 flex flex-col", children: _jsx(CalendarShellContext.Provider, { value: contextValue, children: children }) }));
|
|
102
102
|
}
|
|
103
|
-
return (_jsx(AppShell, { active: "calendar", userUid: userUid, authServerUrl: authServerUrl, impersonating: impersonating, impersonationBaseUrl: impersonationBaseUrl, trusted: trusted, children: inner }));
|
|
103
|
+
return (_jsx(AppShell, { active: "calendar", userUid: userUid, authServerUrl: authServerUrl, impersonating: impersonating, impersonationBaseUrl: impersonationBaseUrl, trusted: trusted, pluginNav: pluginNav, children: inner }));
|
|
104
104
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Contact } from "@rapidmx/react-shared/contacts/contactsApi.js";
|
|
3
|
+
export interface ContactDetailPaneProps {
|
|
4
|
+
contact: Contact;
|
|
5
|
+
onEdit: () => void;
|
|
6
|
+
onDelete: () => void;
|
|
7
|
+
/** Present only on the mobile detail route — renders a "back to contacts" link above the header. Absent
|
|
8
|
+
* on the desktop inline pane, which never navigates away (selecting a different contact just swaps
|
|
9
|
+
* `contact` in place). */
|
|
10
|
+
backHref?: string;
|
|
11
|
+
/** Called after one of the contact's key changes was accepted or kept, or turned out to be stale - the caller
|
|
12
|
+
* re-reads the contact. */
|
|
13
|
+
onKeysChanged?: () => void;
|
|
14
|
+
/** `false` when the reader is known not to be able to change this contact's keys (the actions are hidden); `undefined`
|
|
15
|
+
* when unknown (they're hidden after a 403). */
|
|
16
|
+
canResolveKeys?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A contact's read-only detail view. Shared by the desktop inline pane (`apps/www/contacts/index.tsx`,
|
|
20
|
+
* always visible alongside the contact list) and the mobile detail route
|
|
21
|
+
* (`apps/www/contacts/[uid].tsx`, a full page on its own reached by tapping a contact row).
|
|
22
|
+
*/
|
|
23
|
+
export default function ContactDetailPane({ contact, onEdit, onDelete, backHref, onKeysChanged, canResolveKeys }: ContactDetailPaneProps): React.JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Contact } from "@rapidmx/react-shared/contacts/contactsApi.js";
|
|
3
|
+
import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
4
|
+
export interface ContactFormProps {
|
|
5
|
+
contact?: Contact;
|
|
6
|
+
/** Create mode: the default mailbox (and its Contacts folder) to create in. */
|
|
7
|
+
mailboxUid?: string;
|
|
8
|
+
folderUid?: string;
|
|
9
|
+
/** Create mode: every mailbox the contact could be created in - with more than one, a Mailbox selector
|
|
10
|
+
* is shown. Choosing a mailbox other than `mailboxUid` looks up that mailbox's own Contacts folder. */
|
|
11
|
+
mailboxes?: Mailbox[];
|
|
12
|
+
onSaved: (contact: Contact) => void;
|
|
13
|
+
onCancel: () => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A contact's create/edit form. Shared by the desktop inline pane (`apps/www/contacts/index.tsx`) and the
|
|
17
|
+
* mobile detail route's own edit mode (`apps/www/contacts/[uid].tsx`) — creation itself stays
|
|
18
|
+
* desktop-and-mobile-inline (see that route's own doc comment on why "new" never gets a dedicated route).
|
|
19
|
+
*/
|
|
20
|
+
export default function ContactForm({ contact, mailboxUid, folderUid, mailboxes, onSaved, onCancel }: ContactFormProps): React.JSX.Element;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Contact } from "@rapidmx/react-shared/contacts/contactsApi.js";
|
|
3
|
+
export type ContactsView = {
|
|
4
|
+
type: "all";
|
|
5
|
+
} | {
|
|
6
|
+
type: "favorites";
|
|
7
|
+
} | {
|
|
8
|
+
type: "list";
|
|
9
|
+
uid: string;
|
|
10
|
+
name: string;
|
|
11
|
+
} | {
|
|
12
|
+
type: "deleted";
|
|
13
|
+
} | {
|
|
14
|
+
type: "category";
|
|
15
|
+
name: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function contactsViewKey(view: ContactsView): string;
|
|
18
|
+
export interface ContactsSidebarProps {
|
|
19
|
+
mailboxUid?: string;
|
|
20
|
+
/** The caller's own (non-deleted) contacts — used to compute counts and the distinct category list.
|
|
21
|
+
* Deliberately not fetched again here; the parent already has this loaded for the main table. */
|
|
22
|
+
contacts: Contact[];
|
|
23
|
+
active: ContactsView;
|
|
24
|
+
onSelect: (view: ContactsView) => void;
|
|
25
|
+
/** Whether to offer the "Deleted" view - restapi only honors `deleted=true` for a caller with delete and
|
|
26
|
+
* update rights on the mailbox (anyone else silently gets the live contacts), so the parent hides it
|
|
27
|
+
* otherwise. Defaults to `true`. */
|
|
28
|
+
showDeleted?: boolean;
|
|
29
|
+
/** Bumped by the parent whenever a contact-list membership changes, so a newly-created/renamed list
|
|
30
|
+
* shows up without a full page reload. */
|
|
31
|
+
refreshToken?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Contacts' Outlook-style left sidebar: "Your contacts" (all, with count), "Favorites", "Your contact
|
|
35
|
+
* lists" (real `ContactList` records, fetched here), "Deleted", and "Categories" (derived from the
|
|
36
|
+
* distinct `categories` values across the loaded contacts — no separate category registry exists
|
|
37
|
+
* server-side). Owns its own contact-list data-fetching, per this app's convention of keeping
|
|
38
|
+
* `ContactsShellContext` itself minimal (mailbox/folder resolution only).
|
|
39
|
+
*/
|
|
40
|
+
export default function ContactsSidebar({ mailboxUid, contacts, active, onSelect, refreshToken, showDeleted }: ContactsSidebarProps): React.JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface ContactsToolbarProps {
|
|
3
|
+
selectedCount: number;
|
|
4
|
+
/** `true` only when every currently-selected contact is already favorited — flips the "favorite" button
|
|
5
|
+
* to act (and read) as a removal instead of an addition. */
|
|
6
|
+
allSelectedFavorited: boolean;
|
|
7
|
+
onNewContact: () => void;
|
|
8
|
+
onEdit: () => void;
|
|
9
|
+
onDelete: () => void;
|
|
10
|
+
onEmail: () => void;
|
|
11
|
+
onToggleFavorite: () => void;
|
|
12
|
+
onAddCategory: () => void;
|
|
13
|
+
onExportVCard: () => void;
|
|
14
|
+
/** A `.vcf` file the user picked via the toolbar's own hidden file input — parsing/import is the
|
|
15
|
+
* page's responsibility (it has the mailbox/folder context and the actual `createContact` calls). */
|
|
16
|
+
onImportFile: (file: File) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Contacts' Outlook-style ribbon toolbar. Deliberately presentational — every action is a callback the
|
|
20
|
+
* page (`apps/www/contacts/index.tsx`) implements, since it owns the actual contact data, the current
|
|
21
|
+
* selection, and the mailbox/folder context every real action (create/delete/email/favorite/category)
|
|
22
|
+
* needs. "New contact list" isn't duplicated here — `ContactsSidebar` already has its own inline
|
|
23
|
+
* "+ New list" affordance next to the "Your contact lists" heading.
|
|
24
|
+
*/
|
|
25
|
+
export default function ContactsToolbar({ selectedCount, allSelectedFavorited, onNewContact, onEdit, onDelete, onEmail, onToggleFavorite, onAddCategory, onExportVCard, onImportFile, }: ContactsToolbarProps): React.JSX.Element;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type KeyConflict } from "@rapidmx/react-shared/crypto/keyvaultApi.js";
|
|
3
|
+
export interface KeyChangeReviewProps {
|
|
4
|
+
/** The mailbox whose contact holds the pinned key. */
|
|
5
|
+
mailboxUid: string;
|
|
6
|
+
/** The contact's address `resolveKeyConflict()` looks the contact up by; absent hides the actions. */
|
|
7
|
+
address?: string;
|
|
8
|
+
useType: "sign" | "encrypt";
|
|
9
|
+
/** Who the key belongs to, for the advice ("confirm with <name>"). */
|
|
10
|
+
ownerName: string;
|
|
11
|
+
/** The currently pinned key and when it was first trusted; absent (couldn't be loaded) hides the actions. */
|
|
12
|
+
current?: {
|
|
13
|
+
fingerprint: string;
|
|
14
|
+
since: number;
|
|
15
|
+
};
|
|
16
|
+
/** The new key: its fingerprint, the addresses its certificate names, and when and how it was recorded (absent when
|
|
17
|
+
* it's only known from the message being read). */
|
|
18
|
+
proposed: {
|
|
19
|
+
fingerprint: string;
|
|
20
|
+
emails?: string[];
|
|
21
|
+
observedAt?: number;
|
|
22
|
+
source?: KeyConflict["source"];
|
|
23
|
+
};
|
|
24
|
+
/** For accepting a key seen in a message: its certificate. Omitted, accepting pins the recorded conflict's key. */
|
|
25
|
+
certificate?: string;
|
|
26
|
+
/** Whether "Keep current key" applies - only when a conflict for this exact key is recorded. */
|
|
27
|
+
canReject: boolean;
|
|
28
|
+
/** `false` when the reader is known to lack the rights to change the contact's keys. */
|
|
29
|
+
canResolve?: boolean;
|
|
30
|
+
/** After a successful accept or reject - the caller refreshes what it shows. */
|
|
31
|
+
onResolved: (action: "accept" | "reject") => void;
|
|
32
|
+
/** After a 409: the pinned key changed meanwhile. The caller reloads and says so (`KEY_CHANGE_STALE_MESSAGE`). */
|
|
33
|
+
onPinnedKeyChanged: () => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The old-versus-new comparison for a changed contact key, with Accept new key (confirmed in a dialog) and Keep current
|
|
37
|
+
* key, calling `resolveKeyConflict()` against the pinned fingerprint shown. Shared by a message's "signing key changed"
|
|
38
|
+
* notice and a contact's recorded key conflicts.
|
|
39
|
+
*/
|
|
40
|
+
export default function KeyChangeReview({ mailboxUid, address, useType, ownerName, current, proposed, certificate, canReject, canResolve, onResolved, onPinnedKeyChanged, }: KeyChangeReviewProps): React.JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Contact } from "@rapidmx/react-shared/contacts/contactsApi.js";
|
|
2
|
+
import { type KeyConflict, type PublicKey } from "@rapidmx/react-shared/crypto/keyvaultApi.js";
|
|
3
|
+
export declare const KEY_CHANGE_STALE_MESSAGE = "This contact's keys changed while you were looking; reload to see the latest.";
|
|
4
|
+
export declare const KEY_CHANGE_INVALID_MESSAGE = "The new key isn't a valid, current certificate for this address, so it can't be accepted. Nothing was changed.";
|
|
5
|
+
export declare const KEY_CHANGE_FORBIDDEN_MESSAGE = "You don't have permission to change this contact's keys.";
|
|
6
|
+
export declare const KEY_CHANGE_NOT_FOUND_MESSAGE = "There's no key change to resolve for this address any more. It may already have been resolved, or the contact was removed.";
|
|
7
|
+
export declare const KEY_CHANGE_GENERIC_MESSAGE = "Couldn't update this contact's keys. Try again.";
|
|
8
|
+
/** The error text for a failed `resolveKeyConflict()` other than `PinnedKeyChangedError` (which the caller reloads
|
|
9
|
+
* on and reports with `KEY_CHANGE_STALE_MESSAGE`). */
|
|
10
|
+
export declare function keyChangeErrorMessage(err: unknown): string;
|
|
11
|
+
/** Groups a fingerprint into 4-character blocks (`ab12 cd34 ...`), ignoring any separators it came with, so it can be
|
|
12
|
+
* read out and compared over another channel. */
|
|
13
|
+
export declare function groupFingerprint(fingerprint: string): string;
|
|
14
|
+
/** Whether two fingerprints name the same key (case and separators ignored). */
|
|
15
|
+
export declare function sameFingerprint(a: string | undefined, b: string | undefined): boolean;
|
|
16
|
+
/** A revoked key's label: `"superseded"` for a routine replacement, `"revoked"` for a compromised key or one revoked
|
|
17
|
+
* with no reason; `undefined` when the key isn't revoked. */
|
|
18
|
+
export declare function revocationLabel(key: PublicKey): "superseded" | "revoked" | undefined;
|
|
19
|
+
/** Where a key conflict was observed, for display. */
|
|
20
|
+
export declare function conflictSourceLabel(source: KeyConflict["source"]): string;
|
|
21
|
+
/** restapi records `keysFirstSeen` on a contact but react-shared's `Contact` type doesn't carry it. */
|
|
22
|
+
type ContactWithFirstSeen = Pick<Contact, "previousKeys"> & {
|
|
23
|
+
keysFirstSeen?: number;
|
|
24
|
+
};
|
|
25
|
+
/** When `key` (one of `contact`'s pinned keys) started being trusted: when it replaced the newest previous key of its
|
|
26
|
+
* use, else the contact's `keysFirstSeen`, else the key's own `notBefore`. */
|
|
27
|
+
export declare function keyPinnedSince(contact: ContactWithFirstSeen, key: PublicKey): number;
|
|
28
|
+
export declare function formatDate(ms: number): string;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React, { PropsWithChildren } from "react";
|
|
2
|
+
import { Mailbox } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
3
|
+
import { AppShellProps } from "../../layout/AppShell.js";
|
|
4
|
+
export type ContactsShellProps = Omit<AppShellProps, "active">;
|
|
5
|
+
export interface ContactsShellContextValue {
|
|
6
|
+
/** The mailbox currently selected (`?mailboxUid=`, or the caller's first accessible mailbox). */
|
|
7
|
+
mailboxUid?: string;
|
|
8
|
+
/** The selected mailbox's single `contacts`-type folder. */
|
|
9
|
+
folderUid?: string;
|
|
10
|
+
mailboxes: Mailbox[];
|
|
11
|
+
}
|
|
12
|
+
/** Reads the mailbox/folder Contacts is currently showing, as resolved by the enclosing `ContactsShell`. */
|
|
13
|
+
export declare function useContactsShell(): ContactsShellContextValue;
|
|
14
|
+
/**
|
|
15
|
+
* The Contacts app's shell: unlike `MailShell` (a folder tree, since a mailbox has many mail folders),
|
|
16
|
+
* a mailbox has exactly one well-known `contacts` folder — see `BaseMailboxRoute.create()`'s eager
|
|
17
|
+
* provisioning in `@rapidmx/restapi`, which guarantees it exists for every mailbox. This shell's own
|
|
18
|
+
* sidebar is therefore just a mailbox switcher (when the caller has more than one); the actual contact
|
|
19
|
+
* list/detail UI is `apps/www/contacts/index.tsx`'s own content, rendered as `children` here.
|
|
20
|
+
*/
|
|
21
|
+
export default function ContactsShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, pluginNav, children, }: PropsWithChildren<ContactsShellProps>): React.JSX.Element;
|
|
@@ -24,7 +24,7 @@ export function useContactsShell() {
|
|
|
24
24
|
* sidebar is therefore just a mailbox switcher (when the caller has more than one); the actual contact
|
|
25
25
|
* list/detail UI is `apps/www/contacts/index.tsx`'s own content, rendered as `children` here.
|
|
26
26
|
*/
|
|
27
|
-
export default function ContactsShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, children, }) {
|
|
27
|
+
export default function ContactsShell({ userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, pluginNav, children, }) {
|
|
28
28
|
const [status, setStatus] = useState("checking");
|
|
29
29
|
const [error, setError] = useState(null);
|
|
30
30
|
const [mailboxes, setMailboxes] = useState([]);
|
|
@@ -89,5 +89,5 @@ export default function ContactsShell({ userUid, authServerUrl, impersonating, i
|
|
|
89
89
|
}, children: mailboxes.map((mb) => (_jsxs("option", { value: mb.uid, children: [mb.displayName, mb.ownerUserUid ? "" : " (shared)"] }, mb.uid))) })] })), folderError && _jsx(Alert, { children: folderError })] }));
|
|
90
90
|
inner = (_jsxs(_Fragment, { children: [_jsx("aside", { className: "hidden md:flex w-56 shrink-0 bg-surface border-r border-border flex-col p-3 gap-3", children: sidebarContent("desktop") }), _jsx(Drawer, { open: drawerOpen, onClose: () => setDrawerOpen(false), title: "Mailbox", children: _jsx("div", { className: "flex flex-col gap-3", children: sidebarContent("mobile") }) }), _jsxs("main", { className: "flex-1 min-w-0 flex flex-col", children: [(mailboxes.length > 1 || folderError) && (_jsx("button", { type: "button", className: "md:hidden m-3 w-9 h-9 flex items-center justify-center rounded-sm text-text-muted hover:bg-surface-alt hover:text-text", "aria-label": "Open mailbox switcher", onClick: () => setDrawerOpen(true), children: _jsx(HiOutlineBars3, { size: 20, "aria-hidden": "true" }) })), _jsx(ContactsShellContext.Provider, { value: contextValue, children: children })] })] }));
|
|
91
91
|
}
|
|
92
|
-
return (_jsx(AppShell, { active: "contacts", userUid: userUid, authServerUrl: authServerUrl, impersonating: impersonating, impersonationBaseUrl: impersonationBaseUrl, trusted: trusted, children: inner }));
|
|
92
|
+
return (_jsx(AppShell, { active: "contacts", userUid: userUid, authServerUrl: authServerUrl, impersonating: impersonating, impersonationBaseUrl: impersonationBaseUrl, trusted: trusted, pluginNav: pluginNav, children: inner }));
|
|
93
93
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import "../../../styles/app.css";
|
|
2
|
+
import React, { PropsWithChildren } from "react";
|
|
3
|
+
export type EscrowSection = "matters" | "auditLog";
|
|
4
|
+
export interface EscrowShellProps {
|
|
5
|
+
active: EscrowSection;
|
|
6
|
+
userUid?: string;
|
|
7
|
+
authServerUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Gates every `apps/escrow` page behind "is this an authenticated caller at all" — deliberately **not**
|
|
11
|
+
* the same "does a canary endpoint 403 me" pattern `AdminShell` uses for the trusted `admin` role.
|
|
12
|
+
* `specs/end-to-end_encryption.md`'s "Separation of duties" makes holder-ness a per-`EscrowScope` grant
|
|
13
|
+
* (`EscrowScope.holderUserUids`), checked directly against `holderUserUids` in every restapi route that
|
|
14
|
+
* needs it (`requireEscrowHolder()`) — never a role, and never global. There is no canary endpoint a
|
|
15
|
+
* non-holder gets a clean 403 from: `GET /escrow/matters` (`BaseMatterRoute.find()`) returns `200 []` for
|
|
16
|
+
* *any* authenticated caller who holds no scope at all, the same as it would for a holder of zero matters
|
|
17
|
+
* — so unlike `AdminShell`, this shell cannot distinguish "holds nothing" from "holds no scope at all" up
|
|
18
|
+
* front, and does not try to. It only confirms the caller is signed in and the API is reachable; every
|
|
19
|
+
* actual holder-gated action (create a Matter, approve/deny a request, read material) is enforced
|
|
20
|
+
* server-side and surfaces its own 403 `ApiRequestError` on the specific page that attempted it, same as
|
|
21
|
+
* any other gated action elsewhere in this codebase. A non-holder who reaches `/escrow` simply sees an
|
|
22
|
+
* empty Matters list and gets a real error if they try to act on something they don't hold — this is a
|
|
23
|
+
* deliberate, documented scope trim rather than an oversight (see this session's own report on it).
|
|
24
|
+
*/
|
|
25
|
+
export default function EscrowShell({ active, userUid, authServerUrl, children }: PropsWithChildren<EscrowShellProps>): React.JSX.Element;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface StringListFieldProps {
|
|
3
|
+
label: string;
|
|
4
|
+
/** Associates the label with the add-input via `htmlFor`/`id`, same as `FormField` — required so the
|
|
5
|
+
* field is reachable via `getByLabelText()` in tests and by assistive tech, not just visually. */
|
|
6
|
+
id: string;
|
|
7
|
+
values: string[];
|
|
8
|
+
onChange: (values: string[]) => void;
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
emptyMessage?: string;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A plain, controlled add/remove list-of-strings editor — the same visual add-row/per-item-remove shape
|
|
15
|
+
* `MemberListCard` already established for a `DistributionList`'s `memberAddresses`, but operating on a
|
|
16
|
+
* local `value`/`onChange` pair rather than persisting itself via its own API call. `MemberListCard`
|
|
17
|
+
* isn't reusable here as-is: `EscrowScope.holderUserUids` (at least one required) and
|
|
18
|
+
* `Matter.custodianMailboxUids` (also non-empty) are both required *at creation time*, before there's any
|
|
19
|
+
* uid yet to `PUT` against — this needs to compose into an ordinary create/edit form's own local state
|
|
20
|
+
* instead, same as every other field on those forms.
|
|
21
|
+
*/
|
|
22
|
+
export default function StringListField({ label, id, values, onChange, placeholder, emptyMessage, disabled, }: StringListFieldProps): React.JSX.Element;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import "../../styles/app.css";
|
|
2
|
+
import React, { PropsWithChildren } from "react";
|
|
3
|
+
import type { IconType } from "react-icons";
|
|
4
|
+
import { NavItem } from "@rapidmx/react-shared/components/navigation/BottomTabBar.js";
|
|
5
|
+
import { PluginNav, PluginNavProps } from "../../plugins/pluginNav.js";
|
|
6
|
+
/** How long sign-out waits for auth-server's logout before navigating anyway. */
|
|
7
|
+
export declare const LOGOUT_TIMEOUT_MS = 3000;
|
|
8
|
+
export type AppShellApp = "mail" | "calendar" | "contacts" | "tasks";
|
|
9
|
+
/** `"settings"` is a valid `active` value but deliberately has no entry in `APPS` below — Settings is
|
|
10
|
+
* reached via a `UserMenu` item, not a 5th rail icon (see `SettingsShell.tsx`), so it highlights no
|
|
11
|
+
* rail/tab icon at all; only the header title needs to account for it. Any other string is a plugin's
|
|
12
|
+
* `appRail` item id (see `PluginNav`). */
|
|
13
|
+
export type AppShellActive = AppShellApp | "settings" | (string & {});
|
|
14
|
+
export interface AppShellProps extends PluginNavProps {
|
|
15
|
+
/** Which icon in the rail is highlighted as the current app — `"settings"` highlights none, and a plugin
|
|
16
|
+
* app page passes its own `appRail` item id. */
|
|
17
|
+
active: AppShellActive;
|
|
18
|
+
/** Populated automatically by the framework from an authenticated request (e.g. a valid `jwt` cookie). */
|
|
19
|
+
userUid?: string;
|
|
20
|
+
/** auth-server's base URL, injected via the route's `fetchProps`. */
|
|
21
|
+
authServerUrl?: string;
|
|
22
|
+
/**
|
|
23
|
+
* `true` when this session is an admin "log in as user" impersonation (a `jwt_impersonator` cookie is
|
|
24
|
+
* present) — see `MailShell`'s original doc comment, unchanged now that this lives here. Drives the
|
|
25
|
+
* "you are viewing as this user — stop impersonating" banner below.
|
|
26
|
+
*/
|
|
27
|
+
impersonating?: boolean;
|
|
28
|
+
/** Where `mailApi.ts`'s `stopImpersonating()` should call — see `MailShell`'s original doc comment. */
|
|
29
|
+
impersonationBaseUrl?: string;
|
|
30
|
+
/** `true` when the caller's JWT carries a trusted role — shows an "Admin" item in the user menu below. */
|
|
31
|
+
trusted?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface AppDef {
|
|
34
|
+
id: AppShellApp;
|
|
35
|
+
href: string;
|
|
36
|
+
label: string;
|
|
37
|
+
icon: IconType;
|
|
38
|
+
}
|
|
39
|
+
export declare const APPS: AppDef[];
|
|
40
|
+
/** `APPS` followed by the plugins' `appRail` items (generic icon), core ids winning - see `mergePluginNavItems`. */
|
|
41
|
+
export declare function appRailItems(pluginNav?: PluginNav): NavItem[];
|
|
42
|
+
/**
|
|
43
|
+
* The persistent chrome shared by every webmail app (Mail, Calendar, Contacts, Tasks): a left icon rail for
|
|
44
|
+
* switching apps (full page loads — see `MailShell`'s original doc comment on this framework having no
|
|
45
|
+
* client-side router), a header with the current app's name and `UserMenu`, and the impersonation banner.
|
|
46
|
+
* Each app's own shell (e.g. `MailShell`) renders its own contextual sidebar + content as `children`, inside
|
|
47
|
+
* the area to the right of the icon rail and below the header.
|
|
48
|
+
*/
|
|
49
|
+
export default function AppShell({ active, userUid, authServerUrl, impersonating, impersonationBaseUrl, trusted, pluginNav, children, }: PropsWithChildren<AppShellProps>): React.JSX.Element;
|