@rapidmx/web-client 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/apps/shared/components/admin/layout/AdminShell.tsx +25 -8
- 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 +36 -18
- package/apps/shared/components/mail/layout/MailShell.tsx +420 -418
- package/apps/shared/components/settings/layout/SettingsShell.tsx +23 -9
- 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/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 +9 -0
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +12 -0
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +12 -0
- 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 +10 -0
- package/dist/apps/shared/components/admin/settings/PluginsManager.d.ts +4 -0
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +9 -0
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.d.ts +14 -0
- package/dist/apps/shared/components/admin/setup/SetupWizard.d.ts +36 -0
- 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 +11 -0
- package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +23 -0
- package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +93 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +58 -0
- 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/EmojiPicker.d.ts +14 -0
- package/dist/apps/shared/components/mail/compose/GifPicker.d.ts +13 -0
- package/dist/apps/shared/components/mail/compose/RichTextEditor.d.ts +34 -0
- package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.d.ts +16 -0
- package/dist/apps/shared/components/mail/compose/composeFlushRegistry.d.ts +27 -0
- package/dist/apps/shared/components/mail/layout/MailShell.d.ts +57 -0
- package/dist/apps/shared/components/mail/layout/MailShell.js +2 -2
- 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 -9
- package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +41 -0
- package/dist/apps/shared/components/tasks/TasksToolbar.d.ts +14 -0
- package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +24 -0
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +2 -2
- package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -0
- package/dist/apps/shared/mail/listAllPages.d.ts +17 -0
- package/dist/apps/shared/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/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/messages/[uid].d.ts +11 -0
- 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/focused-inbox/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/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
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { ParsedSearchQuery } from "@rapidmx/react-shared/search/queryGrammar.js";
|
|
2
|
+
import { LocalIndexEntity } from "./localIndexSchema.js";
|
|
3
|
+
/** One request sent from the main thread. `id` correlates the eventual response - `postMessage` has no
|
|
4
|
+
* native request/response pairing, so every RPC call carries its own id (see `localIndexRpcClient.ts`,
|
|
5
|
+
* the main-thread counterpart that generates/awaits these). */
|
|
6
|
+
export interface LocalIndexRequest {
|
|
7
|
+
id: number;
|
|
8
|
+
method: "init" | "indexEntities" | "removeEntity" | "moveEntity" | "indexedVersions" | "pruneEntities" | "search" | "coverage" | "setWindow" | "setBuilding" | "destroy" | "destroyAll" | "selfTest" | "ping";
|
|
9
|
+
params?: unknown;
|
|
10
|
+
}
|
|
11
|
+
export type LocalIndexResponse = {
|
|
12
|
+
id: number;
|
|
13
|
+
ok: true;
|
|
14
|
+
result: unknown;
|
|
15
|
+
} | {
|
|
16
|
+
id: number;
|
|
17
|
+
ok: false;
|
|
18
|
+
error: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Carried by every call a build pass makes (`init`, `setWindow`, `setBuilding`, `indexEntities`,
|
|
22
|
+
* `pruneEntities`). Generations come from one monotonic counter on the main thread
|
|
23
|
+
* (`localIndexRpcClient.ts`), shared with `destroy`/`destroyAll`: a call whose generation is older than the
|
|
24
|
+
* last destroy of its mailbox is rejected (`StaleGenerationError`), so a build started before a lock/sign-out
|
|
25
|
+
* can't recreate the index with its captured keys or write into a rebuild that started after it. Calls
|
|
26
|
+
* without a generation (search, coverage, in-session moves) are never rejected.
|
|
27
|
+
*/
|
|
28
|
+
export interface GenerationParams {
|
|
29
|
+
generation?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface InitParams extends GenerationParams {
|
|
32
|
+
mailboxUid: string;
|
|
33
|
+
/** The mailbox's already-derived local-index key (`localIndexKey.ts`'s `deriveLocalIndexKey()`) -
|
|
34
|
+
* this Worker never touches the master key itself, only this one purpose-derived value, matching
|
|
35
|
+
* every other MK-derived-key boundary already established elsewhere in this codebase. */
|
|
36
|
+
indexKey: Uint8Array;
|
|
37
|
+
}
|
|
38
|
+
export interface IndexEntitiesParams extends GenerationParams {
|
|
39
|
+
mailboxUid: string;
|
|
40
|
+
entities: LocalIndexEntity[];
|
|
41
|
+
}
|
|
42
|
+
export interface IndexEntitiesResult {
|
|
43
|
+
/** `true` when this call's eviction pass had to delete anything to get back under the byte budget. */
|
|
44
|
+
budgetReached: boolean;
|
|
45
|
+
/** The eviction watermark after this call - see `WindowState.evictedBefore`. */
|
|
46
|
+
evictedBefore?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface WindowState {
|
|
49
|
+
/** Set once the byte budget has forced an eviction: messages dated strictly before this were evicted (or
|
|
50
|
+
* would be, on insert), so a build pass neither re-fetches them nor walks past them. Cleared by
|
|
51
|
+
* `setWindow` once the index has comfortably shrunk back under budget (or the budget was raised). */
|
|
52
|
+
evictedBefore?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface DestroyParams extends GenerationParams {
|
|
55
|
+
mailboxUid: string;
|
|
56
|
+
}
|
|
57
|
+
export interface RemoveEntityParams {
|
|
58
|
+
mailboxUid: string;
|
|
59
|
+
entityUid: string;
|
|
60
|
+
}
|
|
61
|
+
export interface MoveEntityParams {
|
|
62
|
+
mailboxUid: string;
|
|
63
|
+
entityUid: string;
|
|
64
|
+
folderUid: string;
|
|
65
|
+
}
|
|
66
|
+
export interface IndexedVersionsParams {
|
|
67
|
+
mailboxUid: string;
|
|
68
|
+
entityUids: string[];
|
|
69
|
+
}
|
|
70
|
+
export interface PruneEntitiesParams extends GenerationParams {
|
|
71
|
+
mailboxUid: string;
|
|
72
|
+
/** Every entity uid a build pass saw on the server. */
|
|
73
|
+
keepEntityUids: string[];
|
|
74
|
+
/** Only rows at or after this `date_for_sort` are candidates (the walk's own time floor - anything
|
|
75
|
+
* older was never re-listed, so its absence proves nothing). `undefined` means no floor. */
|
|
76
|
+
since?: string;
|
|
77
|
+
/** Only rows currently filed in one of these folders are candidates - folders whose listing was reliable
|
|
78
|
+
* for the whole walk. `undefined` means every folder. */
|
|
79
|
+
folderUids?: string[];
|
|
80
|
+
}
|
|
81
|
+
export interface SearchParams {
|
|
82
|
+
mailboxUid: string;
|
|
83
|
+
parsed: ParsedSearchQuery;
|
|
84
|
+
limit: number;
|
|
85
|
+
/** How many matches to skip before returning `limit` more - §8's composite pagination cursor's own
|
|
86
|
+
* "the local index position consumed so far" component, threaded straight through to SQL `OFFSET`.
|
|
87
|
+
* Defaults to `0` for a first page. */
|
|
88
|
+
offset?: number;
|
|
89
|
+
}
|
|
90
|
+
export interface LocalSearchHit {
|
|
91
|
+
entityUid: string;
|
|
92
|
+
/** Raw `bm25()` score - more negative is a better match, per SQLite's own convention. Normalized by
|
|
93
|
+
* `searchTier2.ts` (main thread) before merging (spec §7). */
|
|
94
|
+
score: number;
|
|
95
|
+
snippet?: string;
|
|
96
|
+
}
|
|
97
|
+
export interface LocalSearchPage {
|
|
98
|
+
hits: LocalSearchHit[];
|
|
99
|
+
/** `true` when at least one more match exists beyond this page - detected by fetching `limit + 1`
|
|
100
|
+
* rows and trimming the extra one, rather than a separate `COUNT(*)` query. */
|
|
101
|
+
hasMore: boolean;
|
|
102
|
+
}
|
|
103
|
+
export interface Coverage {
|
|
104
|
+
/** The oldest date the index is known to cover. While `complete`, every indexable message at least
|
|
105
|
+
* this recent is present; otherwise it is only the oldest row that happens to be present. `undefined`
|
|
106
|
+
* for an empty index. */
|
|
107
|
+
indexedFrom?: string;
|
|
108
|
+
indexedCount: number;
|
|
109
|
+
building: boolean;
|
|
110
|
+
/** `true` only once a build pass *in this connection's lifetime* has walked every mail folder all the
|
|
111
|
+
* way back to its time floor with no errors and no budget cut-off - reset whenever the index is opened,
|
|
112
|
+
* so a completion persisted by an earlier session never counts. Callers MUST NOT treat `indexedFrom` as
|
|
113
|
+
* a coverage guarantee (e.g. to narrow Tier 3) unless this is `true` and `building` is `false`. */
|
|
114
|
+
complete: boolean;
|
|
115
|
+
/** Only set while `complete`: the guarantee ends here. Nothing indexes mail that arrives after a pass
|
|
116
|
+
* started, so everything dated after this (ISO) is NOT covered and must still be searched elsewhere. */
|
|
117
|
+
indexedUntil?: string;
|
|
118
|
+
}
|
|
119
|
+
export interface SetWindowParams extends GenerationParams {
|
|
120
|
+
mailboxUid: string;
|
|
121
|
+
timeFloorMonths: number;
|
|
122
|
+
byteBudgetBytes: number;
|
|
123
|
+
}
|
|
124
|
+
export interface SetBuildingParams extends GenerationParams {
|
|
125
|
+
mailboxUid: string;
|
|
126
|
+
building: boolean;
|
|
127
|
+
/** Only meaningful with `building: false` - whether the pass that just ended walked everything. */
|
|
128
|
+
complete?: boolean;
|
|
129
|
+
/** Only meaningful with `complete: true` - the pass's time floor (ISO), or `undefined` for none. */
|
|
130
|
+
coveredFrom?: string;
|
|
131
|
+
/** Only meaningful with `complete: true` - when the pass started (ISO, minus a clock-skew margin). */
|
|
132
|
+
coveredUntil?: string;
|
|
133
|
+
}
|
|
134
|
+
/** Runs `task` after every previously queued task for `mailboxUid` has settled. Exported for tests. */
|
|
135
|
+
export declare function runExclusive<T>(mailboxUid: string, task: () => Promise<T>): Promise<T>;
|
|
136
|
+
/** Rejects a build-pass call issued before its mailbox's index was last destroyed. */
|
|
137
|
+
export declare class StaleGenerationError extends Error {
|
|
138
|
+
constructor(mailboxUid: string);
|
|
139
|
+
}
|
|
140
|
+
/** Thrown by `init` when another tab already holds this mailbox's index open. */
|
|
141
|
+
export declare class LocalIndexBusyError extends Error {
|
|
142
|
+
constructor(mailboxUid: string);
|
|
143
|
+
}
|
|
144
|
+
/** Routes one request to its handler, inside the owning mailbox's serial queue. Exported for tests. */
|
|
145
|
+
export declare function handleRequest(method: LocalIndexRequest["method"], params: unknown): Promise<unknown>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier 2 (`specs/search.md` §6) - the local encrypted index's public entry point, mirroring
|
|
3
|
+
* `@rapidmx/react-shared`'s `searchTier3.ts#searchEncryptedCandidates()` in shape: given a parsed query
|
|
4
|
+
* and this session's unlocked keys, return already-scored, already-sourced results plus how much of the
|
|
5
|
+
* mailbox the local index actually covers right now.
|
|
6
|
+
*
|
|
7
|
+
* Returns `{ results: [], coverage: undefined }` - never throws, never rejects - both when `unlocked` is
|
|
8
|
+
* absent (nothing is indexed without a master key to derive the index key from, the same "silently
|
|
9
|
+
* contributes nothing" degradation `searchTier3.ts`'s own doc comment describes for its identical case)
|
|
10
|
+
* and when anything about the local index itself fails (Worker/WASM/OPFS unsupported or unavailable in
|
|
11
|
+
* this browser, a corrupted index mid-rebuild, a query timeout). This tier's own storage engine is
|
|
12
|
+
* strictly best-effort infrastructure Tier 1 doesn't depend on - a caller `Promise.all()`-ing this
|
|
13
|
+
* alongside Tier 1/Tier 3 (`apps/www/index.tsx`'s own search orchestration) must never see the *whole
|
|
14
|
+
* search* fail just because this one, most novel piece had a bad moment.
|
|
15
|
+
*/
|
|
16
|
+
import type { UnlockedKeys } from "@rapidmx/react-shared/crypto/keySession.js";
|
|
17
|
+
import type { ParsedSearchQuery } from "@rapidmx/react-shared/search/queryGrammar.js";
|
|
18
|
+
import type { SearchResult } from "@rapidmx/react-shared/search/searchApi.js";
|
|
19
|
+
import type { Coverage } from "./localIndexWorker.js";
|
|
20
|
+
export interface Tier2SearchOutcome {
|
|
21
|
+
results: SearchResult[];
|
|
22
|
+
/** `undefined` when `unlocked` was absent - there's no local index to report coverage for at all in
|
|
23
|
+
* that case, distinct from a real, empty (just-built) index. */
|
|
24
|
+
coverage?: Coverage;
|
|
25
|
+
/** `true` when at least one more match exists beyond `results` - §8's composite pagination cursor's
|
|
26
|
+
* own per-tier continuation signal. Always `false` alongside the `unlocked`-absent/error
|
|
27
|
+
* degradations below - there is nothing more to page through either way. */
|
|
28
|
+
hasMore: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function searchLocalIndex(mailboxUid: string, parsed: ParsedSearchQuery, unlocked: UnlockedKeys | undefined, limit?: number, offset?: number): Promise<Tier2SearchOutcome>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React, { PropsWithChildren } from "react";
|
|
2
|
+
import { Branding } from "@rapidmx/react-shared/branding/brandingApi.js";
|
|
3
|
+
export interface LayoutProps {
|
|
4
|
+
/**
|
|
5
|
+
* Supplied by `WwwRoute`'s `fetchProps()` override (`src/mongo/routes/wwwRoute.ts`/
|
|
6
|
+
* `src/sql/routes/wwwRoute.ts`) - `@rapidrest/react` spreads the same merged page props onto this
|
|
7
|
+
* layout as onto the page it wraps, so it renders correctly server-side on the very first byte of the
|
|
8
|
+
* response: no client-side flash from the stock defaults below, and a crawler reading the raw HTML
|
|
9
|
+
* sees the real branding too. `undefined` only on the framework's unwrapped `_500` fallback path,
|
|
10
|
+
* which doesn't receive props at all - falling back to the stock defaults there is acceptable.
|
|
11
|
+
*/
|
|
12
|
+
branding?: Branding;
|
|
13
|
+
}
|
|
14
|
+
export default function Layout({ children, branding }: PropsWithChildren<LayoutProps>): React.JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ContactsShellProps } from "../../shared/components/contacts/layout/ContactsShell.js";
|
|
3
|
+
/**
|
|
4
|
+
* Only reached on mobile (below the `md` breakpoint) — desktop's `apps/www/contacts/index.tsx` keeps its
|
|
5
|
+
* existing inline detail pane and never navigates here; see that file's `handleSelectRow`. There is no
|
|
6
|
+
* equivalent "new contact" route — an unsaved contact has no uid to route on, so creation stays an
|
|
7
|
+
* in-place mode-switch on every device (see `apps/www/contacts/index.tsx`'s `handleNew`).
|
|
8
|
+
*/
|
|
9
|
+
export default function ContactDetailPage(props: ContactsShellProps & {
|
|
10
|
+
params: {
|
|
11
|
+
uid: string;
|
|
12
|
+
};
|
|
13
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { MailShellProps } from "../../shared/components/mail/layout/MailShell.js";
|
|
3
|
+
/**
|
|
4
|
+
* Only reached on mobile (below the `md` breakpoint) — desktop's `apps/www/index.tsx` keeps its existing
|
|
5
|
+
* inline reading pane and never navigates here; see that file's `handleSelect`.
|
|
6
|
+
*/
|
|
7
|
+
export default function MessageDetailPage(props: MailShellProps & {
|
|
8
|
+
params: {
|
|
9
|
+
uid: string;
|
|
10
|
+
};
|
|
11
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type SettingsAutoReplyPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
+
export default function SettingsAutoReplyPage(props: SettingsAutoReplyPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
/** Mirrors restapi's `BaseKeyVaultRoute` `MAX_MASTER_KEY_WRAPS` - `addMasterKeyWrap()` refuses a vault
|
|
4
|
+
* already holding this many wraps (escrow included). */
|
|
5
|
+
export declare const MAX_MASTER_KEY_WRAPS = 20;
|
|
6
|
+
export type SettingsEncryptionPageProps = Omit<SettingsShellProps, "active">;
|
|
7
|
+
export default function SettingsEncryptionPage(props: SettingsEncryptionPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type MailFilterDetailPageProps = Omit<SettingsShellProps, "active"> & {
|
|
4
|
+
params: {
|
|
5
|
+
uid: string;
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
export default function MailFilterDetailPage(props: MailFilterDetailPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Folder } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
2
|
+
import { MailFilterAction } from "@rapidmx/react-shared/mail/mailFilterRulesApi.js";
|
|
3
|
+
import { ActionTypeDef, ConditionFieldDef } from "../../../shared/components/rules/RuleBuilder.js";
|
|
4
|
+
export declare const MAIL_FILTER_CONDITION_FIELDS: ConditionFieldDef[];
|
|
5
|
+
/**
|
|
6
|
+
* Builds the mail-filter action-type registry for `RuleBuilder`, closing over `folders` so the
|
|
7
|
+
* move/copy-to-folder actions can render a real folder picker. `RuleBuilder`'s own `render()` contract
|
|
8
|
+
* is synchronous, so this resolves `folders` once up front (the caller already loads them for the page
|
|
9
|
+
* itself) rather than each action row fetching independently.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildMailFilterActionTypes(folders: Folder[]): ActionTypeDef<MailFilterAction>[];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type SettingsFiltersPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
+
export default function SettingsFiltersPage(props: SettingsFiltersPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type NewMailFilterPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
+
export default function NewMailFilterPage(props: NewMailFilterPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type SettingsFocusedInboxPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
+
export default function SettingsFocusedInboxPage(props: SettingsFocusedInboxPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type SettingsLabelsPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
+
export default function SettingsLabelsPage(props: SettingsLabelsPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type SettingsPrivacyPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
+
export default function SettingsPrivacyPage(props: SettingsPrivacyPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type SettingsReadReceiptsPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
+
export default function SettingsReadReceiptsPage(props: SettingsReadReceiptsPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type SettingsSharingPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
+
export default function SettingsSharingPage(props: SettingsSharingPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type SignatureDetailPageProps = Omit<SettingsShellProps, "active"> & {
|
|
4
|
+
params: {
|
|
5
|
+
uid: string;
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
export default function SignatureDetailPage(props: SignatureDetailPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type SettingsSignaturesPageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
+
export default function SettingsSignaturesPage(props: SettingsSignaturesPageProps): React.JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SettingsShellProps } from "../../../../shared/components/settings/layout/SettingsShell.js";
|
|
3
|
+
export type NewSignaturePageProps = Omit<SettingsShellProps, "active">;
|
|
4
|
+
export default function NewSignaturePage(props: NewSignaturePageProps): React.JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MailSignature } from "@rapidmx/react-shared/mail/mailSignaturesApi.js";
|
|
2
|
+
/**
|
|
3
|
+
* `MailSignature.isDefaultForNewMessages`/`isDefaultForReplyForward` are each meant to hold on at most
|
|
4
|
+
* one signature per mailbox — enforced by convention, not a database constraint (see
|
|
5
|
+
* `mailSignaturesApi.ts`'s own doc comment, quoting restapi's). Before saving a signature with one of
|
|
6
|
+
* these flags newly turned on, this turns the flag off on whichever *other* signature in `signatures`
|
|
7
|
+
* currently has it — the "the composing client toggles the previous default off" responsibility that
|
|
8
|
+
* convention assigns to a caller like the New/Edit signature forms.
|
|
9
|
+
*/
|
|
10
|
+
export declare function clearPreviousDefaults(signatures: MailSignature[], savingUid: string | undefined, isDefaultForNewMessages: boolean, isDefaultForReplyForward: boolean): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rapidmx/web-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "RapidMX's webmail and admin console React UI for web-based clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": "https://github.com/rapidmx/web-client",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"./shared/styles/app.css": "./apps/shared/styles/app.css"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
-
"build": "rimraf dist && tsc -p tsconfig.json && shx mkdir -p dist/apps/shared/styles && shx cp apps/shared/styles/app.css dist/apps/shared/styles/app.css",
|
|
20
|
+
"build": "rimraf dist && tsc -p tsconfig.json && shx mkdir -p dist/apps/shared/styles && shx cp apps/shared/styles/app.css dist/apps/shared/styles/app.css && node scripts/checkDistReferences.mjs",
|
|
21
21
|
"lint": "eslint ./apps ./test",
|
|
22
22
|
"test": "vitest run --coverage",
|
|
23
23
|
"test:watch": "vitest --watch --coverage",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@dnd-kit/core": "^6.3.1",
|
|
40
40
|
"@journeyapps/wa-sqlite": "^2.0.4",
|
|
41
41
|
"@peculiar/x509": "^2.1.0",
|
|
42
|
-
"@rapidmx/react-shared": "
|
|
42
|
+
"@rapidmx/react-shared": "^0.6.0",
|
|
43
43
|
"@tiptap/core": "^3.31.3",
|
|
44
44
|
"@tiptap/extension-highlight": "^3.31.3",
|
|
45
45
|
"@tiptap/extension-image": "^3.31.3",
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
-
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
-
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
-
import React, { useState } from "react";
|
|
6
|
-
import { BookingAvailabilityWindow } from "@rapidmx/react-shared/booking/bookingApi.js";
|
|
7
|
-
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
8
|
-
|
|
9
|
-
const DAY_LABELS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
10
|
-
const INPUT_CLASS =
|
|
11
|
-
"text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
12
|
-
|
|
13
|
-
function minutesToTimeValue(minutes: number): string {
|
|
14
|
-
const h = Math.floor(minutes / 60)
|
|
15
|
-
.toString()
|
|
16
|
-
.padStart(2, "0");
|
|
17
|
-
const m = (minutes % 60).toString().padStart(2, "0");
|
|
18
|
-
return `${h}:${m}`;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function timeValueToMinutes(value: string): number {
|
|
22
|
-
const [h, m] = value.split(":").map(Number);
|
|
23
|
-
return h * 60 + m;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface AvailabilityEditorProps {
|
|
27
|
-
value: BookingAvailabilityWindow[];
|
|
28
|
-
onChange: (value: BookingAvailabilityWindow[]) => void;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Adds/removes `BookingType.availability` weekly windows — deliberately does not cover `dateOverrides`
|
|
33
|
-
* (per-date blackouts/exceptions), a separate, less commonly needed escape hatch left for a future pass;
|
|
34
|
-
* `BookingUtils.generateCandidateSlots()` already handles an empty `dateOverrides` array gracefully.
|
|
35
|
-
*/
|
|
36
|
-
export default function AvailabilityEditor({ value, onChange }: AvailabilityEditorProps) {
|
|
37
|
-
const [dayOfWeek, setDayOfWeek] = useState(1);
|
|
38
|
-
const [startTime, setStartTime] = useState("09:00");
|
|
39
|
-
const [endTime, setEndTime] = useState("17:00");
|
|
40
|
-
const [error, setError] = useState<string | null>(null);
|
|
41
|
-
|
|
42
|
-
function handleAdd() {
|
|
43
|
-
const startMinute = timeValueToMinutes(startTime);
|
|
44
|
-
const endMinute = timeValueToMinutes(endTime);
|
|
45
|
-
if (startMinute >= endMinute) {
|
|
46
|
-
setError("Start time must be before end time.");
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
setError(null);
|
|
50
|
-
onChange([...value, { dayOfWeek, startMinute, endMinute }]);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function handleRemove(index: number) {
|
|
54
|
-
onChange(value.filter((_, i) => i !== index));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<div className="flex flex-col gap-3">
|
|
59
|
-
{error && <p className="text-sm text-danger">{error}</p>}
|
|
60
|
-
{value.length === 0 ? (
|
|
61
|
-
<p className="text-sm text-text-muted">No availability windows yet — add one below.</p>
|
|
62
|
-
) : (
|
|
63
|
-
<ul className="flex flex-col gap-2">
|
|
64
|
-
{value.map((window, index) => (
|
|
65
|
-
<li
|
|
66
|
-
key={index}
|
|
67
|
-
className="flex items-center justify-between gap-3 py-2 px-3 border border-border rounded-sm text-sm"
|
|
68
|
-
>
|
|
69
|
-
<span>
|
|
70
|
-
{DAY_LABELS[window.dayOfWeek]} {minutesToTimeValue(window.startMinute)}–
|
|
71
|
-
{minutesToTimeValue(window.endMinute)}
|
|
72
|
-
</span>
|
|
73
|
-
<button
|
|
74
|
-
type="button"
|
|
75
|
-
onClick={() => handleRemove(index)}
|
|
76
|
-
className="text-sm text-danger hover:underline"
|
|
77
|
-
>
|
|
78
|
-
Remove
|
|
79
|
-
</button>
|
|
80
|
-
</li>
|
|
81
|
-
))}
|
|
82
|
-
</ul>
|
|
83
|
-
)}
|
|
84
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
85
|
-
<select
|
|
86
|
-
aria-label="Day of week"
|
|
87
|
-
value={dayOfWeek}
|
|
88
|
-
onChange={(e) => setDayOfWeek(Number(e.target.value))}
|
|
89
|
-
className={INPUT_CLASS}
|
|
90
|
-
>
|
|
91
|
-
{DAY_LABELS.map((label, i) => (
|
|
92
|
-
<option key={label} value={i}>
|
|
93
|
-
{label}
|
|
94
|
-
</option>
|
|
95
|
-
))}
|
|
96
|
-
</select>
|
|
97
|
-
<input
|
|
98
|
-
type="time"
|
|
99
|
-
aria-label="Start time"
|
|
100
|
-
value={startTime}
|
|
101
|
-
onChange={(e) => setStartTime(e.target.value)}
|
|
102
|
-
className={INPUT_CLASS}
|
|
103
|
-
/>
|
|
104
|
-
<span className="text-sm text-text-muted">to</span>
|
|
105
|
-
<input
|
|
106
|
-
type="time"
|
|
107
|
-
aria-label="End time"
|
|
108
|
-
value={endTime}
|
|
109
|
-
onChange={(e) => setEndTime(e.target.value)}
|
|
110
|
-
className={INPUT_CLASS}
|
|
111
|
-
/>
|
|
112
|
-
<Button type="button" variant="secondary" className="!w-auto" onClick={handleAdd}>
|
|
113
|
-
Add window
|
|
114
|
-
</Button>
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
);
|
|
118
|
-
}
|