@rapidmx/web-client 0.20.0 → 0.21.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 +381 -381
- package/apps/admin/branding/index.tsx +39 -39
- package/apps/admin/data-requests/index.tsx +482 -482
- package/apps/admin/domains/[uid].tsx +166 -166
- package/apps/admin/escrow-scopes/[uid].tsx +350 -350
- package/apps/admin/index.tsx +129 -129
- package/apps/admin/mailboxes/[uid].tsx +271 -271
- package/apps/admin/mailboxes/new/index.tsx +30 -30
- package/apps/admin/plugins/index.tsx +15 -15
- package/apps/admin/retention-policy/index.tsx +39 -39
- package/apps/admin/signing-certificates/index.tsx +343 -343
- package/apps/escrow/audit-log/index.tsx +196 -196
- package/apps/escrow/matters/[uid].tsx +621 -621
- package/apps/shared/auth/adminAccess.ts +99 -99
- package/apps/shared/components/admin/layout/AdminShell.tsx +384 -384
- package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +238 -238
- package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +152 -152
- package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +194 -194
- package/apps/shared/components/admin/settings/BrandingForm.tsx +423 -423
- package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +119 -119
- package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +198 -198
- package/apps/shared/components/admin/settings/PluginsManager.tsx +2093 -2093
- package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +182 -182
- package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +288 -288
- package/apps/shared/components/admin/setup/SetupWizard.tsx +446 -446
- package/apps/shared/components/admin/usePagedList.tsx +129 -129
- package/apps/shared/components/calendar/EventModal.tsx +206 -206
- package/apps/shared/components/calendar/MonthView.tsx +185 -185
- package/apps/shared/components/calendar/RecurrenceEditor.tsx +227 -227
- package/apps/shared/components/calendar/SplitDayView.tsx +144 -144
- package/apps/shared/components/calendar/TimeGridView.tsx +246 -246
- package/apps/shared/components/calendar/allDay.ts +124 -124
- package/apps/shared/components/contacts/ContactForm.tsx +383 -383
- package/apps/shared/components/contacts/ContactsToolbar.tsx +103 -103
- package/apps/shared/components/escrow/layout/EscrowShell.tsx +155 -155
- package/apps/shared/components/layout/AppShell.tsx +463 -461
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -398
- package/apps/shared/components/layout/MailboxProvisioning.tsx +168 -168
- package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -315
- package/apps/shared/components/layout/ThemeSwitch.tsx +84 -84
- package/apps/shared/components/layout/UserMenu.tsx +439 -439
- package/apps/shared/components/mail/ConversationList.tsx +332 -332
- package/apps/shared/components/mail/ConversationThreadPane.tsx +613 -613
- package/apps/shared/components/mail/MailSelectionBar.tsx +240 -240
- package/apps/shared/components/mail/MenuButton.tsx +404 -404
- package/apps/shared/components/mail/MessageDetailPane.tsx +1757 -1757
- package/apps/shared/components/mail/compose/ComposeContext.tsx +312 -312
- package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -422
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +1705 -1681
- package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -147
- package/apps/shared/components/mail/compose/composeFlushRegistry.ts +60 -60
- package/apps/shared/components/mail/compose/quotedBody.ts +161 -161
- package/apps/shared/components/mail/reading/MessageMoreMenu.tsx +258 -258
- package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +79 -79
- package/apps/shared/components/mail/reading/messageExport.ts +59 -59
- package/apps/shared/components/mail/reading/printMessage.ts +99 -99
- package/apps/shared/components/mail/reading/useMessageActions.ts +443 -443
- package/apps/shared/components/mail/verificationSeals.ts +125 -125
- package/apps/shared/components/rules/RuleBuilder.tsx +311 -311
- package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -344
- package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -51
- package/apps/shared/keyboard/ShortcutProvider.tsx +62 -62
- package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -84
- package/apps/shared/keyboard/dispatch.ts +124 -124
- package/apps/shared/keyboard/format.ts +89 -89
- package/apps/shared/keyboard/keymap.ts +114 -114
- package/apps/shared/keyboard/registry.ts +65 -65
- package/apps/shared/keyboard/targets.ts +79 -79
- package/apps/shared/mail/folderOfType.ts +49 -49
- package/apps/shared/mail/folderTree.ts +143 -143
- package/apps/shared/mail/listAllPages.ts +39 -39
- package/apps/shared/mail/newMailNotifications.ts +171 -171
- package/apps/shared/mail/outbox/sendJob.ts +445 -445
- package/apps/shared/mail/outbox/sendOutcomes.ts +155 -155
- package/apps/shared/mail/reportNotices.ts +66 -66
- package/apps/shared/mail/senderBlocking.ts +141 -141
- package/apps/shared/mail/useMailConnection.ts +205 -205
- package/apps/shared/mail/useMailLiveUpdates.ts +277 -277
- package/apps/shared/mail/useMailboxUpdateAccess.ts +60 -60
- package/apps/shared/mail/useMarkMessageRead.ts +47 -47
- package/apps/shared/mail/useNewMailNotifications.ts +178 -178
- package/apps/shared/notifications/store.ts +560 -560
- package/apps/shared/search/LocalIndexLifecycle.tsx +114 -114
- package/apps/shared/search/localIndexBuilder.ts +481 -481
- package/apps/shared/signing/enrollmentStorage.ts +33 -33
- package/apps/shared/signing/enrollmentTracker.ts +385 -385
- package/apps/shared/signing/enrollmentView.ts +251 -251
- package/apps/shared/signing/useNow.ts +19 -19
- package/apps/shared/signing/useSigningEnrollmentWatcher.ts +90 -90
- package/apps/shared/styles/app.css +396 -396
- package/apps/www/calendar/index.tsx +581 -581
- package/apps/www/contacts/[uid].tsx +112 -112
- package/apps/www/index.tsx +3012 -3012
- package/apps/www/messages/[uid].tsx +139 -139
- package/apps/www/settings/auto-reply/index.tsx +136 -136
- package/apps/www/settings/blocked-senders/index.tsx +303 -303
- package/apps/www/settings/encryption/index.tsx +1290 -1290
- package/apps/www/settings/filters/[uid].tsx +179 -179
- package/apps/www/settings/filters/index.tsx +105 -105
- package/apps/www/settings/filters/new/index.tsx +165 -165
- package/apps/www/settings/labels/index.tsx +207 -207
- package/apps/www/settings/privacy/index.tsx +495 -495
- package/apps/www/settings/profile/index.tsx +251 -251
- package/apps/www/settings/read-receipts/index.tsx +150 -150
- package/apps/www/settings/sharing/index.tsx +259 -259
- package/apps/www/settings/signatures/[uid].tsx +175 -175
- package/apps/www/settings/signatures/index.tsx +91 -91
- package/apps/www/settings/signatures/new/index.tsx +138 -138
- package/apps/www/tasks/index.tsx +654 -654
- package/dist/apps/shared/components/admin/layout/AdminShell.js +2 -2
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +3 -3
- package/dist/apps/shared/components/escrow/layout/EscrowShell.js +2 -2
- package/dist/apps/shared/components/layout/AppShell.js +4 -2
- package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +7 -1
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +20 -1
- package/dist/apps/shared/components/mail/reading/printMessage.js +11 -11
- package/dist/apps/shared/styles/app.css +396 -396
- package/package.json +2 -2
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
-
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
-
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
-
import { pageTitle } from "../../shared/navigation/pageTitle.js";
|
|
6
|
-
import { useNavigate } from "../../shared/navigation/index.js";
|
|
7
|
-
import React, { useEffect, useState } from "react";
|
|
8
|
-
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
9
|
-
import { Contact, deleteContact, getContact } from "@rapidmx/react-shared/contacts/contactsApi.js";
|
|
10
|
-
import ContactsShell, { ContactsShellProps } from "../../shared/components/contacts/layout/ContactsShell.js";
|
|
11
|
-
import ContactDetailPane from "../../shared/components/contacts/ContactDetailPane.js";
|
|
12
|
-
import ContactForm from "../../shared/components/contacts/ContactForm.js";
|
|
13
|
-
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
14
|
-
import { clearPinnedSignerCache } from "../../shared/components/mail/pinnedSigners.js";
|
|
15
|
-
import { notifyApiError } from "../../shared/notifications/apiErrors.js";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Only reached on mobile (below the `md` breakpoint) — desktop's `apps/www/contacts/index.tsx` keeps its
|
|
19
|
-
* existing inline detail pane and never navigates here; see that file's `handleSelectRow`. There is no
|
|
20
|
-
* equivalent "new contact" route — an unsaved contact has no uid to route on, so creation stays an
|
|
21
|
-
* in-place mode-switch on every device (see `apps/www/contacts/index.tsx`'s `handleNew`).
|
|
22
|
-
*/
|
|
23
|
-
function ContactDetailPage(props: ContactsShellProps & { params: { uid: string } }) {
|
|
24
|
-
return (
|
|
25
|
-
<ContactsShell {...props}>
|
|
26
|
-
<ContactDetailContent uid={props.params.uid} />
|
|
27
|
-
</ContactsShell>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
type Mode = "view" | "edit";
|
|
32
|
-
|
|
33
|
-
function ContactDetailContent({ uid }: { uid: string }) {
|
|
34
|
-
const navigate = useNavigate();
|
|
35
|
-
const [contact, setContact] = useState<Contact | null>(null);
|
|
36
|
-
const [loading, setLoading] = useState(true);
|
|
37
|
-
const [error, setError] = useState<string | null>(null);
|
|
38
|
-
const [mode, setMode] = useState<Mode>("view");
|
|
39
|
-
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
setLoading(true);
|
|
42
|
-
setError(null);
|
|
43
|
-
getContact(uid)
|
|
44
|
-
.then(setContact)
|
|
45
|
-
.catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load this contact."))
|
|
46
|
-
.finally(() => setLoading(false));
|
|
47
|
-
}, [uid]);
|
|
48
|
-
|
|
49
|
-
// Takes `contact` as a parameter rather than reading it from the closure/state — mirrors the desktop
|
|
50
|
-
// pane's own `handleDelete` in `apps/www/contacts/index.tsx`. The only caller (below) is only ever
|
|
51
|
-
// rendered once `contact` is already resolved (see the `error || !contact` guard above it), so this
|
|
52
|
-
// never runs with a stale/absent contact — no redundant null check needed here.
|
|
53
|
-
async function handleDelete(contact: Contact) {
|
|
54
|
-
try {
|
|
55
|
-
await deleteContact(contact.uid, contact.version);
|
|
56
|
-
// A deleted contact's pinned signing keys must stop vouching for signatures.
|
|
57
|
-
clearPinnedSignerCache();
|
|
58
|
-
navigate("/contacts");
|
|
59
|
-
} catch (err) {
|
|
60
|
-
// A pop-up: a failed delete leaves the contact on screen rather than replacing the page with an error.
|
|
61
|
-
notifyApiError(err, "Couldn't delete the contact");
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// After a key change was resolved (or found stale): re-read the contact, keeping the one shown if that fails.
|
|
66
|
-
function handleKeysChanged() {
|
|
67
|
-
getContact(uid).then(setContact, () => undefined);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (loading) {
|
|
71
|
-
return <p className="p-8 text-sm text-text-muted">Loading…</p>;
|
|
72
|
-
}
|
|
73
|
-
if (error || !contact) {
|
|
74
|
-
return <Alert>{error ?? "Contact not found."}</Alert>;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const backHref = "/contacts";
|
|
78
|
-
if (mode === "edit") {
|
|
79
|
-
return (
|
|
80
|
-
<div>
|
|
81
|
-
<a href={backHref} className="text-sm text-primary-dark hover:underline block px-6 pt-6">
|
|
82
|
-
← Back to contacts
|
|
83
|
-
</a>
|
|
84
|
-
<ContactForm
|
|
85
|
-
contact={contact}
|
|
86
|
-
onSaved={(saved) => {
|
|
87
|
-
setContact(saved);
|
|
88
|
-
setMode("view");
|
|
89
|
-
}}
|
|
90
|
-
onCancel={() => setMode("view")}
|
|
91
|
-
/>
|
|
92
|
-
</div>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<div className="p-6">
|
|
98
|
-
<ContactDetailPane
|
|
99
|
-
contact={contact}
|
|
100
|
-
onEdit={() => setMode("edit")}
|
|
101
|
-
onDelete={() => handleDelete(contact)}
|
|
102
|
-
backHref={backHref}
|
|
103
|
-
onKeysChanged={handleKeysChanged}
|
|
104
|
-
/>
|
|
105
|
-
</div>
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export default ContactDetailPage;
|
|
110
|
-
|
|
111
|
-
/** The tab's title: `Brand: Contacts` (see `pageTitle()`). */
|
|
112
|
-
export const title = pageTitle("Contacts");
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
import { pageTitle } from "../../shared/navigation/pageTitle.js";
|
|
6
|
+
import { useNavigate } from "../../shared/navigation/index.js";
|
|
7
|
+
import React, { useEffect, useState } from "react";
|
|
8
|
+
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
9
|
+
import { Contact, deleteContact, getContact } from "@rapidmx/react-shared/contacts/contactsApi.js";
|
|
10
|
+
import ContactsShell, { ContactsShellProps } from "../../shared/components/contacts/layout/ContactsShell.js";
|
|
11
|
+
import ContactDetailPane from "../../shared/components/contacts/ContactDetailPane.js";
|
|
12
|
+
import ContactForm from "../../shared/components/contacts/ContactForm.js";
|
|
13
|
+
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
14
|
+
import { clearPinnedSignerCache } from "../../shared/components/mail/pinnedSigners.js";
|
|
15
|
+
import { notifyApiError } from "../../shared/notifications/apiErrors.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Only reached on mobile (below the `md` breakpoint) — desktop's `apps/www/contacts/index.tsx` keeps its
|
|
19
|
+
* existing inline detail pane and never navigates here; see that file's `handleSelectRow`. There is no
|
|
20
|
+
* equivalent "new contact" route — an unsaved contact has no uid to route on, so creation stays an
|
|
21
|
+
* in-place mode-switch on every device (see `apps/www/contacts/index.tsx`'s `handleNew`).
|
|
22
|
+
*/
|
|
23
|
+
function ContactDetailPage(props: ContactsShellProps & { params: { uid: string } }) {
|
|
24
|
+
return (
|
|
25
|
+
<ContactsShell {...props}>
|
|
26
|
+
<ContactDetailContent uid={props.params.uid} />
|
|
27
|
+
</ContactsShell>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type Mode = "view" | "edit";
|
|
32
|
+
|
|
33
|
+
function ContactDetailContent({ uid }: { uid: string }) {
|
|
34
|
+
const navigate = useNavigate();
|
|
35
|
+
const [contact, setContact] = useState<Contact | null>(null);
|
|
36
|
+
const [loading, setLoading] = useState(true);
|
|
37
|
+
const [error, setError] = useState<string | null>(null);
|
|
38
|
+
const [mode, setMode] = useState<Mode>("view");
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
setLoading(true);
|
|
42
|
+
setError(null);
|
|
43
|
+
getContact(uid)
|
|
44
|
+
.then(setContact)
|
|
45
|
+
.catch((err) => setError(err instanceof ApiRequestError ? err.message : "Could not load this contact."))
|
|
46
|
+
.finally(() => setLoading(false));
|
|
47
|
+
}, [uid]);
|
|
48
|
+
|
|
49
|
+
// Takes `contact` as a parameter rather than reading it from the closure/state — mirrors the desktop
|
|
50
|
+
// pane's own `handleDelete` in `apps/www/contacts/index.tsx`. The only caller (below) is only ever
|
|
51
|
+
// rendered once `contact` is already resolved (see the `error || !contact` guard above it), so this
|
|
52
|
+
// never runs with a stale/absent contact — no redundant null check needed here.
|
|
53
|
+
async function handleDelete(contact: Contact) {
|
|
54
|
+
try {
|
|
55
|
+
await deleteContact(contact.uid, contact.version);
|
|
56
|
+
// A deleted contact's pinned signing keys must stop vouching for signatures.
|
|
57
|
+
clearPinnedSignerCache();
|
|
58
|
+
navigate("/contacts");
|
|
59
|
+
} catch (err) {
|
|
60
|
+
// A pop-up: a failed delete leaves the contact on screen rather than replacing the page with an error.
|
|
61
|
+
notifyApiError(err, "Couldn't delete the contact");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// After a key change was resolved (or found stale): re-read the contact, keeping the one shown if that fails.
|
|
66
|
+
function handleKeysChanged() {
|
|
67
|
+
getContact(uid).then(setContact, () => undefined);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (loading) {
|
|
71
|
+
return <p className="p-8 text-sm text-text-muted">Loading…</p>;
|
|
72
|
+
}
|
|
73
|
+
if (error || !contact) {
|
|
74
|
+
return <Alert>{error ?? "Contact not found."}</Alert>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const backHref = "/contacts";
|
|
78
|
+
if (mode === "edit") {
|
|
79
|
+
return (
|
|
80
|
+
<div>
|
|
81
|
+
<a href={backHref} className="text-sm text-primary-dark hover:underline block px-6 pt-6">
|
|
82
|
+
← Back to contacts
|
|
83
|
+
</a>
|
|
84
|
+
<ContactForm
|
|
85
|
+
contact={contact}
|
|
86
|
+
onSaved={(saved) => {
|
|
87
|
+
setContact(saved);
|
|
88
|
+
setMode("view");
|
|
89
|
+
}}
|
|
90
|
+
onCancel={() => setMode("view")}
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="p-6">
|
|
98
|
+
<ContactDetailPane
|
|
99
|
+
contact={contact}
|
|
100
|
+
onEdit={() => setMode("edit")}
|
|
101
|
+
onDelete={() => handleDelete(contact)}
|
|
102
|
+
backHref={backHref}
|
|
103
|
+
onKeysChanged={handleKeysChanged}
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default ContactDetailPage;
|
|
110
|
+
|
|
111
|
+
/** The tab's title: `Brand: Contacts` (see `pageTitle()`). */
|
|
112
|
+
export const title = pageTitle("Contacts");
|