@rapidmx/web-client 0.3.0 → 0.4.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/apps/admin/branding/index.tsx +39 -404
- package/apps/admin/domains/[uid].tsx +92 -259
- package/apps/admin/encryption-policy/index.tsx +19 -0
- package/apps/admin/index.tsx +100 -82
- package/apps/admin/mailbox-policy/index.tsx +19 -0
- package/apps/admin/mailboxes/new/index.tsx +28 -291
- package/apps/admin/plugins/index.tsx +15 -0
- package/apps/admin/retention-policy/index.tsx +39 -132
- package/apps/admin/setup/index.tsx +15 -0
- package/apps/shared/components/admin/layout/AdminShell.tsx +249 -210
- package/apps/shared/components/admin/settings/BrandingForm.tsx +374 -0
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +176 -0
- package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +104 -0
- package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +34 -0
- package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +302 -0
- package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +119 -0
- package/apps/shared/components/admin/settings/PluginsManager.tsx +595 -0
- package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +98 -0
- package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +254 -0
- package/apps/shared/components/admin/setup/SetupWizard.tsx +300 -0
- package/apps/shared/components/calendar/CalendarListSidebar.tsx +120 -76
- package/apps/shared/components/calendar/EventModal.tsx +40 -4
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +80 -99
- package/apps/shared/components/contacts/ContactForm.tsx +38 -4
- package/apps/shared/components/layout/AppShell.tsx +198 -169
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +298 -265
- package/apps/shared/components/layout/UnlockPromptProvider.tsx +128 -0
- package/apps/shared/components/mail/MessageDetailPane.tsx +630 -595
- package/apps/shared/components/mail/compose/ComposeContext.tsx +8 -3
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +930 -765
- package/apps/shared/components/mail/layout/MailShell.tsx +408 -302
- package/apps/shared/components/settings/layout/SettingsShell.tsx +1 -0
- package/apps/shared/mail/findWellKnownFolderUid.ts +13 -0
- package/apps/shared/search/LocalIndexLifecycle.tsx +89 -0
- package/apps/shared/search/localIndexBlockCipher.ts +83 -0
- package/apps/shared/search/localIndexBuilder.ts +179 -0
- package/apps/shared/search/localIndexKey.ts +27 -0
- package/apps/shared/search/localIndexRpcClient.ts +116 -0
- package/apps/shared/search/localIndexSchema.ts +227 -0
- package/apps/shared/search/localIndexSizePreference.ts +88 -0
- package/apps/shared/search/localIndexVFS.ts +235 -0
- package/apps/shared/search/localIndexWorker.ts +464 -0
- package/apps/shared/search/searchTier2.ts +79 -0
- package/apps/shared/search/wa-sqlite-shims.d.ts +44 -0
- package/apps/www/calendar/index.tsx +31 -21
- package/apps/www/contacts/index.tsx +33 -6
- package/apps/www/index.tsx +661 -106
- package/apps/www/messages/[uid].tsx +5 -1
- package/apps/www/settings/encryption/index.tsx +63 -1
- package/apps/www/settings/sharing/index.tsx +271 -0
- package/apps/www/tasks/index.tsx +53 -4
- package/dist/apps/admin/branding/index.js +4 -98
- package/dist/apps/admin/domains/[uid].js +5 -68
- package/dist/apps/admin/encryption-policy/index.js +8 -0
- package/dist/apps/admin/index.js +14 -1
- package/dist/apps/admin/mailbox-policy/index.js +8 -0
- package/dist/apps/admin/mailboxes/new/index.js +4 -89
- package/dist/apps/admin/plugins/index.js +6 -0
- package/dist/apps/admin/retention-policy/index.js +3 -36
- package/dist/apps/admin/setup/index.js +6 -0
- package/dist/apps/shared/components/admin/layout/AdminShell.js +34 -3
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +105 -0
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +77 -0
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +57 -0
- package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +25 -0
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +106 -0
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +52 -0
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +260 -0
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +44 -0
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +131 -0
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +142 -0
- package/dist/apps/shared/components/calendar/CalendarListSidebar.js +24 -13
- package/dist/apps/shared/components/calendar/EventModal.js +16 -4
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +47 -39
- package/dist/apps/shared/components/contacts/ContactForm.js +16 -5
- package/dist/apps/shared/components/layout/AppShell.js +30 -7
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +15 -3
- package/dist/apps/shared/components/layout/UnlockPromptProvider.js +77 -0
- package/dist/apps/shared/components/mail/MessageDetailPane.js +27 -2
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +2 -2
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +134 -14
- package/dist/apps/shared/components/mail/layout/MailShell.js +106 -40
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +1 -0
- package/dist/apps/shared/mail/findWellKnownFolderUid.js +12 -0
- package/dist/apps/shared/search/LocalIndexLifecycle.js +76 -0
- package/dist/apps/shared/search/localIndexBlockCipher.js +63 -0
- package/dist/apps/shared/search/localIndexBuilder.js +153 -0
- package/dist/apps/shared/search/localIndexKey.js +25 -0
- package/dist/apps/shared/search/localIndexRpcClient.js +78 -0
- package/dist/apps/shared/search/localIndexSchema.js +179 -0
- package/dist/apps/shared/search/localIndexSizePreference.js +78 -0
- package/dist/apps/shared/search/localIndexVFS.js +230 -0
- package/dist/apps/shared/search/localIndexWorker.js +341 -0
- package/dist/apps/shared/search/searchTier2.js +38 -0
- package/dist/apps/www/calendar/index.js +14 -12
- package/dist/apps/www/contacts/index.js +15 -6
- package/dist/apps/www/index.js +496 -94
- package/dist/apps/www/messages/[uid].js +5 -1
- package/dist/apps/www/settings/encryption/index.js +30 -2
- package/dist/apps/www/settings/sharing/index.js +128 -0
- package/dist/apps/www/tasks/index.js +27 -5
- package/package.json +3 -2
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
startOfWeek,
|
|
20
20
|
} from "date-fns";
|
|
21
21
|
import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
|
|
22
|
-
import { colorForFolder } from "@rapidmx/react-shared/calendar/calendarColors.js";
|
|
23
22
|
import { CalendarEvent, listCalendarEvents } from "@rapidmx/react-shared/calendar/calendarApi.js";
|
|
24
23
|
import { moveOccurrence, resizeOccurrenceEnd } from "@rapidmx/react-shared/calendar/calendarMutations.js";
|
|
25
24
|
import { resolveDragAction } from "@rapidmx/react-shared/calendar/calendarDragIds.js";
|
|
@@ -58,10 +57,7 @@ interface ModalState {
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
function CalendarContent() {
|
|
61
|
-
const { mailboxUid, folderUid, calendarFolders, mailboxes, reloadFolders } = useCalendarShell();
|
|
62
|
-
// `CalendarShell` only ever renders this component once `mailboxUid` is set, and always to a value
|
|
63
|
-
// drawn from `mailboxes` itself (see its own resolution logic) — the lookup below always succeeds.
|
|
64
|
-
const organizerAddress = mailboxes.find((mb) => mb.uid === mailboxUid)!.primarySmtpAddress;
|
|
60
|
+
const { mailboxUid, folderUid, calendarFolders, mailboxCalendars, mailboxes, reloadFolders, colorFor } = useCalendarShell();
|
|
65
61
|
// `PointerSensor` alone activates a drag on the very first touch-move, indistinguishable from a
|
|
66
62
|
// scroll gesture on a touch device. `MouseSensor` (a small `distance` — desktop drags still start
|
|
67
63
|
// immediately on a deliberate movement, no change from before) + `TouchSensor` (a `delay`+`tolerance`
|
|
@@ -100,8 +96,8 @@ function CalendarContent() {
|
|
|
100
96
|
|
|
101
97
|
const checkedFolderUids = checkedFolderUidsState ?? new Set(calendarFolders.map((f) => f.uid));
|
|
102
98
|
const folderColors = useMemo(
|
|
103
|
-
() => Object.fromEntries(calendarFolders.map((f) => [f.uid,
|
|
104
|
-
[calendarFolders],
|
|
99
|
+
() => Object.fromEntries(calendarFolders.map((f) => [f.uid, colorFor(f)])),
|
|
100
|
+
[calendarFolders, colorFor],
|
|
105
101
|
);
|
|
106
102
|
|
|
107
103
|
function toggleCalendar(toggledUid: string) {
|
|
@@ -116,9 +112,8 @@ function CalendarContent() {
|
|
|
116
112
|
});
|
|
117
113
|
}
|
|
118
114
|
|
|
119
|
-
async function handleAddCalendar(name: string, color: string) {
|
|
120
|
-
|
|
121
|
-
const created = await createFolder({ mailboxUid: mailboxUid!, name, type: "calendar", color });
|
|
115
|
+
async function handleAddCalendar(targetMailboxUid: string, name: string, color: string) {
|
|
116
|
+
const created = await createFolder({ mailboxUid: targetMailboxUid, name, type: "calendar", color });
|
|
122
117
|
setCheckedFolderUidsState((prev) => new Set([...(prev ?? calendarFolders.map((f) => f.uid)), created.uid]));
|
|
123
118
|
reloadFolders();
|
|
124
119
|
}
|
|
@@ -198,8 +193,8 @@ function CalendarContent() {
|
|
|
198
193
|
() =>
|
|
199
194
|
calendarFolders
|
|
200
195
|
.filter((f) => checkedFolderUids.has(f.uid))
|
|
201
|
-
.map((f) => ({ folderUid: f.uid, name: f.name, color:
|
|
202
|
-
[calendarFolders, checkedFolderUidsKey],
|
|
196
|
+
.map((f) => ({ folderUid: f.uid, name: f.name, color: colorFor(f) })),
|
|
197
|
+
[calendarFolders, checkedFolderUidsKey, colorFor],
|
|
203
198
|
);
|
|
204
199
|
|
|
205
200
|
function shiftView(direction: 1 | -1) {
|
|
@@ -277,14 +272,27 @@ function CalendarContent() {
|
|
|
277
272
|
? `${format(rangeStart, "MMM d")} – ${format(rangeEnd, "MMM d, yyyy")}`
|
|
278
273
|
: format(viewDate, "EEEE, MMMM d, yyyy");
|
|
279
274
|
|
|
275
|
+
const modalFolderUid = modal ? (modal.targetFolderUid ?? modal.occurrence?.folderUid ?? folderUid) : undefined;
|
|
276
|
+
const modalMailboxUid = calendarFolders.find((f) => f.uid === modalFolderUid)?.mailboxUid ?? mailboxUid;
|
|
277
|
+
const modalMailbox = mailboxes.find((mb) => mb.uid === modalMailboxUid);
|
|
278
|
+
// Mailboxes a new event can be created in - only those with at least one calendar.
|
|
279
|
+
const mailboxOptions = useMemo(
|
|
280
|
+
() =>
|
|
281
|
+
mailboxCalendars
|
|
282
|
+
.filter((mc) => mc.calendarFolders.length > 0)
|
|
283
|
+
.map((mc) => ({ mailbox: mc.mailbox, calendars: mc.calendarFolders.map((f) => ({ uid: f.uid, name: f.name })) })),
|
|
284
|
+
[mailboxCalendars],
|
|
285
|
+
);
|
|
286
|
+
|
|
280
287
|
const sidebarContent = (
|
|
281
288
|
<>
|
|
282
289
|
<MiniDatePicker selected={viewDate} onSelect={(date) => setViewDate(startOfDay(date))} />
|
|
283
290
|
<CalendarListSidebar
|
|
284
|
-
|
|
291
|
+
mailboxCalendars={mailboxCalendars}
|
|
285
292
|
checkedFolderUids={checkedFolderUids}
|
|
286
293
|
onToggle={toggleCalendar}
|
|
287
294
|
onAddCalendar={handleAddCalendar}
|
|
295
|
+
colorFor={colorFor}
|
|
288
296
|
/>
|
|
289
297
|
</>
|
|
290
298
|
);
|
|
@@ -383,17 +391,19 @@ function CalendarContent() {
|
|
|
383
391
|
</DndContext>
|
|
384
392
|
)}
|
|
385
393
|
|
|
386
|
-
{/* `
|
|
387
|
-
|
|
388
|
-
successful, folder-scoped load to render.
|
|
389
|
-
|
|
394
|
+
{/* `folderUid` is guaranteed defined whenever `modal` is: `openNewEvent` only sets it after
|
|
395
|
+
checking it, and `openEvent` only fires from an occurrence that itself required a
|
|
396
|
+
successful, folder-scoped load to render. The modal is scoped to that folder's own mailbox
|
|
397
|
+
(which may be a shared one) - its organizer address and calendar picker come from there. */}
|
|
398
|
+
{modal && modalMailbox && (
|
|
390
399
|
<EventModal
|
|
391
400
|
open
|
|
392
401
|
onClose={closeModal}
|
|
393
|
-
mailboxUid={
|
|
394
|
-
folderUid={
|
|
395
|
-
calendars={calendarFolders.map((f) => ({ uid: f.uid, name: f.name }))}
|
|
396
|
-
|
|
402
|
+
mailboxUid={modalMailbox.uid}
|
|
403
|
+
folderUid={modalFolderUid!}
|
|
404
|
+
calendars={calendarFolders.filter((f) => f.mailboxUid === modalMailbox.uid).map((f) => ({ uid: f.uid, name: f.name }))}
|
|
405
|
+
mailboxOptions={mailboxOptions}
|
|
406
|
+
organizerAddress={modalMailbox.primarySmtpAddress}
|
|
397
407
|
occurrence={modal.occurrence}
|
|
398
408
|
initialStart={modal.initialStart}
|
|
399
409
|
initialEnd={modal.initialEnd}
|
|
@@ -53,7 +53,7 @@ function downloadTextFile(filename: string, content: string): void {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function ContactsContent() {
|
|
56
|
-
const { folderUid, mailboxUid } = useContactsShell();
|
|
56
|
+
const { folderUid, mailboxUid, mailboxes } = useContactsShell();
|
|
57
57
|
const { openCompose } = useCompose();
|
|
58
58
|
const isMobile = useIsMobile();
|
|
59
59
|
const [contacts, setContacts] = useState<Contact[]>([]);
|
|
@@ -69,6 +69,8 @@ function ContactsContent() {
|
|
|
69
69
|
const [checkedUids, setCheckedUids] = useState<Set<string>>(new Set());
|
|
70
70
|
const [selectedUid, setSelectedUid] = useState<string | null>(null);
|
|
71
71
|
const [mode, setMode] = useState<Mode>("view");
|
|
72
|
+
// Set after a contact is created in a different mailbox than the one this list shows.
|
|
73
|
+
const [savedElsewhere, setSavedElsewhere] = useState<{ mailboxUid: string; displayName: string } | null>(null);
|
|
72
74
|
|
|
73
75
|
/** Returns a promise so bulk actions (below) can wait for the refreshed list before re-asserting their
|
|
74
76
|
* own error message — this always clears `error` first (a legitimate reset for a fresh fetch attempt),
|
|
@@ -192,8 +194,15 @@ function ContactsContent() {
|
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
function handleSaved(contact: Contact) {
|
|
195
|
-
setSelectedUid(contact.uid);
|
|
196
197
|
setMode("view");
|
|
198
|
+
if (contact.mailboxUid !== mailboxUid) {
|
|
199
|
+
// Created in another mailbox (via the form's Mailbox selector) - it won't be in this list.
|
|
200
|
+
setSelectedUid(null);
|
|
201
|
+
setSavedElsewhere({ mailboxUid: contact.mailboxUid, displayName: contact.displayName });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
setSavedElsewhere(null);
|
|
205
|
+
setSelectedUid(contact.uid);
|
|
197
206
|
void reload();
|
|
198
207
|
}
|
|
199
208
|
|
|
@@ -237,9 +246,9 @@ function ContactsContent() {
|
|
|
237
246
|
|
|
238
247
|
function handleEmail() {
|
|
239
248
|
const addresses = checkedContacts.map((c) => c.emails[0]?.address).filter((a): a is string => Boolean(a));
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
openCompose({
|
|
249
|
+
// No mailboxUid: a fresh message defaults to the caller's own mailbox, not whichever mailbox's
|
|
250
|
+
// contacts happen to be open - the compose window's From field can still change it.
|
|
251
|
+
openCompose({ to: addresses.join(", ") });
|
|
243
252
|
}
|
|
244
253
|
|
|
245
254
|
async function handleToggleFavorite() {
|
|
@@ -421,8 +430,26 @@ function ContactsContent() {
|
|
|
421
430
|
see handleSelectRow), so this pane only needs to show there for "new", which stays in-place
|
|
422
431
|
on every device (an unsaved contact has no uid for a route). Always visible on desktop. */}
|
|
423
432
|
<div className={["flex-1 min-w-0 overflow-y-auto p-6 md:block", mode === "new" ? "block" : "hidden"].join(" ")}>
|
|
433
|
+
{savedElsewhere && mode !== "new" && (
|
|
434
|
+
<p role="status" className="mb-4 text-sm py-2 px-3 rounded-sm bg-surface-alt text-text">
|
|
435
|
+
{savedElsewhere.displayName} was added to{" "}
|
|
436
|
+
{mailboxes.find((mb) => mb.uid === savedElsewhere.mailboxUid)?.displayName ?? "another mailbox"}.{" "}
|
|
437
|
+
<a
|
|
438
|
+
href={`/contacts?mailboxUid=${encodeURIComponent(savedElsewhere.mailboxUid)}`}
|
|
439
|
+
className="font-medium text-primary-dark hover:underline"
|
|
440
|
+
>
|
|
441
|
+
View that mailbox’s contacts
|
|
442
|
+
</a>
|
|
443
|
+
</p>
|
|
444
|
+
)}
|
|
424
445
|
{mode === "new" && mailboxUid && folderUid ? (
|
|
425
|
-
<ContactForm
|
|
446
|
+
<ContactForm
|
|
447
|
+
mailboxUid={mailboxUid}
|
|
448
|
+
folderUid={folderUid}
|
|
449
|
+
mailboxes={mailboxes}
|
|
450
|
+
onSaved={handleSaved}
|
|
451
|
+
onCancel={handleCancel}
|
|
452
|
+
/>
|
|
426
453
|
) : mode === "edit" && selected ? (
|
|
427
454
|
<ContactForm contact={selected} onSaved={handleSaved} onCancel={handleCancel} />
|
|
428
455
|
) : selected ? (
|