@arbidocs/blocks 0.4.83 → 0.4.85
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/dist/MailBodyEditor-5PIQLYVY.js +217 -0
- package/dist/MailBodyEditor-5PIQLYVY.js.map +1 -0
- package/dist/MailBodyEditor-D7MGJMLH.cjs +223 -0
- package/dist/MailBodyEditor-D7MGJMLH.cjs.map +1 -0
- package/dist/agents/index.cjs +281 -280
- package/dist/agents/index.js +6 -5
- package/dist/agents/workflows/StepBarFrame.d.ts +3 -1
- package/dist/agents/workflows/WorkflowRunner.d.ts +10 -1
- package/dist/agents/workflows/run.d.ts +24 -3
- package/dist/agents/workflows/stepWindow.d.ts +4 -2
- package/dist/agents/workflows/types.d.ts +5 -0
- package/dist/agents/workflows/useWorkflowView.d.ts +16 -1
- package/dist/chat/index.cjs +1 -1
- package/dist/chat/index.js +1 -1
- package/dist/{chunk-FE4ULGOR.js → chunk-2YOVK524.js} +28 -5
- package/dist/chunk-2YOVK524.js.map +1 -0
- package/dist/chunk-3R6XBYL7.cjs +576 -0
- package/dist/chunk-3R6XBYL7.cjs.map +1 -0
- package/dist/{chunk-CKB64Z4R.js → chunk-4XJZ6AAY.js} +6 -245
- package/dist/chunk-4XJZ6AAY.js.map +1 -0
- package/dist/{chunk-SB5ZKOYN.js → chunk-7C35WXYB.js} +4 -63
- package/dist/chunk-7C35WXYB.js.map +1 -0
- package/dist/chunk-7RJTEWYM.js +362 -0
- package/dist/chunk-7RJTEWYM.js.map +1 -0
- package/dist/chunk-AFSOUS5X.cjs +253 -0
- package/dist/chunk-AFSOUS5X.cjs.map +1 -0
- package/dist/{chunk-F4RUCUXF.cjs → chunk-C2HRZSAT.cjs} +2 -63
- package/dist/chunk-C2HRZSAT.cjs.map +1 -0
- package/dist/{chunk-RX35SMPR.js → chunk-FHOZMT5P.js} +7 -6
- package/dist/chunk-FHOZMT5P.js.map +1 -0
- package/dist/{chunk-ZVBPRMHJ.cjs → chunk-JH6JTI76.cjs} +27 -4
- package/dist/chunk-JH6JTI76.cjs.map +1 -0
- package/dist/{chunk-CONIA2ZA.js → chunk-KHY4PYU7.js} +246 -146
- package/dist/chunk-KHY4PYU7.js.map +1 -0
- package/dist/chunk-LGX6UFYG.js +557 -0
- package/dist/chunk-LGX6UFYG.js.map +1 -0
- package/dist/chunk-O4YPVYPO.cjs +373 -0
- package/dist/chunk-O4YPVYPO.cjs.map +1 -0
- package/dist/chunk-R26OHONI.js +250 -0
- package/dist/chunk-R26OHONI.js.map +1 -0
- package/dist/{chunk-RYYYHPFU.cjs → chunk-RZUMQM7W.cjs} +246 -146
- package/dist/chunk-RZUMQM7W.cjs.map +1 -0
- package/dist/{chunk-YJQVLVBJ.cjs → chunk-S5HZBXZA.cjs} +2 -243
- package/dist/chunk-S5HZBXZA.cjs.map +1 -0
- package/dist/{chunk-ZQYE7J3J.cjs → chunk-WRSBM6QN.cjs} +332 -94
- package/dist/chunk-WRSBM6QN.cjs.map +1 -0
- package/dist/{chunk-RSKSXEPW.js → chunk-X23U6QEJ.js} +324 -88
- package/dist/chunk-X23U6QEJ.js.map +1 -0
- package/dist/{chunk-XA5S4776.cjs → chunk-Z7YUCYI6.cjs} +51 -50
- package/dist/chunk-Z7YUCYI6.cjs.map +1 -0
- package/dist/collaboration/index.cjs +100 -49
- package/dist/collaboration/index.js +4 -1
- package/dist/collaboration/mail/MailBodyEditor.d.ts +7 -0
- package/dist/collaboration/mail/MailDeliveryList.d.ts +8 -0
- package/dist/collaboration/mail/MailDocumentPicker.d.ts +15 -0
- package/dist/collaboration/mail/MailGrid.d.ts +17 -0
- package/dist/collaboration/mail/MailMessageView.d.ts +27 -0
- package/dist/collaboration/mail/PlainMailBodyEditor.d.ts +12 -0
- package/dist/collaboration/mail/index.d.ts +6 -0
- package/dist/collaboration/mail/mailCompose.d.ts +24 -0
- package/dist/connect/index.cjs +1684 -72
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.d.ts +2 -1
- package/dist/connect/index.js +1670 -64
- package/dist/connect/index.js.map +1 -1
- package/dist/connection/ConnectedTagTable.d.ts +32 -0
- package/dist/connection/WorkflowRunnerDialogs.d.ts +12 -1
- package/dist/connection/mail/ConnectedMailAccounts.d.ts +21 -0
- package/dist/connection/mail/ConnectedMailComposer.d.ts +40 -0
- package/dist/connection/mail/ConnectedMailSetup.d.ts +16 -0
- package/dist/connection/mail/ConnectedMailbox.d.ts +65 -0
- package/dist/connection/mail/MailComposerForm.d.ts +25 -0
- package/dist/connection/mail/attachments.d.ts +13 -0
- package/dist/connection/mail/index.d.ts +6 -0
- package/dist/connection/mail/mailbox.d.ts +6 -0
- package/dist/connection/mail/types.d.ts +92 -0
- package/dist/connection/useConnectedWorkNotifications.d.ts +6 -0
- package/dist/connection/useWorkflowThread.d.ts +7 -2
- package/dist/document-tools/SignDocumentDialog.d.ts +9 -2
- package/dist/document-tools/index.cjs +2 -2
- package/dist/document-tools/index.js +1 -1
- package/dist/facts/index.cjs +2 -2
- package/dist/facts/index.js +2 -2
- package/dist/files/index.cjs +47 -367
- package/dist/files/index.cjs.map +1 -1
- package/dist/files/index.js +10 -360
- package/dist/files/index.js.map +1 -1
- package/dist/index.cjs +510 -451
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +21 -18
- package/dist/index.js.map +1 -1
- package/dist/settings/index.cjs +10 -9
- package/dist/settings/index.cjs.map +1 -1
- package/dist/settings/index.js +2 -1
- package/dist/settings/index.js.map +1 -1
- package/dist/shell/index.cjs +1 -1
- package/dist/shell/index.js +1 -1
- package/dist/tags/TagColumnsEditor.d.ts +34 -0
- package/dist/tags/TagDetail.d.ts +13 -11
- package/dist/tags/TagTable.d.ts +99 -0
- package/dist/tags/TagsNav.d.ts +2 -2
- package/dist/tags/index.d.ts +7 -5
- package/dist/tags/{tagMatrixDocx.d.ts → tagTableDocx.d.ts} +12 -12
- package/dist/tags/tagTableModel.d.ts +47 -0
- package/dist/workflow-canvas/index.cjs +49 -48
- package/dist/workflow-canvas/index.cjs.map +1 -1
- package/dist/workflow-canvas/index.js +6 -5
- package/dist/workflow-canvas/index.js.map +1 -1
- package/package.json +29 -4
- package/dist/chunk-CKB64Z4R.js.map +0 -1
- package/dist/chunk-CONIA2ZA.js.map +0 -1
- package/dist/chunk-F4RUCUXF.cjs.map +0 -1
- package/dist/chunk-FE4ULGOR.js.map +0 -1
- package/dist/chunk-RSKSXEPW.js.map +0 -1
- package/dist/chunk-RX35SMPR.js.map +0 -1
- package/dist/chunk-RYYYHPFU.cjs.map +0 -1
- package/dist/chunk-SB5ZKOYN.js.map +0 -1
- package/dist/chunk-XA5S4776.cjs.map +0 -1
- package/dist/chunk-YJQVLVBJ.cjs.map +0 -1
- package/dist/chunk-ZQYE7J3J.cjs.map +0 -1
- package/dist/chunk-ZVBPRMHJ.cjs.map +0 -1
- package/dist/connection/ConnectedTagMatrix.d.ts +0 -23
- package/dist/tags/TagMatrix.d.ts +0 -87
- package/dist/tags/tagMatrixModel.d.ts +0 -43
package/dist/connect/index.js
CHANGED
|
@@ -5,32 +5,40 @@ export { ConnectedAssistantDock, redlineStats, textToArtifact, toRedlineMarkdown
|
|
|
5
5
|
import { ConnectedNativeDocxEditor } from '../chunk-A24C6BNA.js';
|
|
6
6
|
import { ConnectionProvider, useConnection } from '../chunk-OYMWV45N.js';
|
|
7
7
|
export { ConnectionProvider, SESSION_EXPIRED_MESSAGE, isAuthenticated, useConnection } from '../chunk-OYMWV45N.js';
|
|
8
|
-
import { SignDocumentDialog } from '../chunk-
|
|
8
|
+
import { SignDocumentDialog } from '../chunk-2YOVK524.js';
|
|
9
9
|
import '../chunk-OPA2Y6YZ.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { FilesFilterBar, FolderFilterButton, FolderPickerRow } from '../chunk-7RJTEWYM.js';
|
|
11
|
+
import { TagTable, tagTableDocx, DOCX_MIME_TYPE } from '../chunk-X23U6QEJ.js';
|
|
12
|
+
import { splitDocRef, WorkflowStepBar, stepLevels, WorkflowRunner, initialRunInputs, workflowSteps, fromPlanSteps, stepStatesFromActivity, toPlanSteps, runThreadTitle, useWorkflowView, stepFields } from '../chunk-KHY4PYU7.js';
|
|
13
|
+
import { TriStateFilterMenu } from '../chunk-R26OHONI.js';
|
|
14
|
+
import { TabHeader } from '../chunk-4WWM3GQ5.js';
|
|
12
15
|
import '../chunk-EVUEIBNU.js';
|
|
16
|
+
import { FilterInput } from '../chunk-YU2KUMGE.js';
|
|
13
17
|
import '../chunk-XD2M6LIG.js';
|
|
14
|
-
import { StreamedArtifactFrame } from '../chunk-LOJVUGBJ.js';
|
|
15
18
|
import '../chunk-VILAPTCB.js';
|
|
19
|
+
import { StreamedArtifactFrame } from '../chunk-LOJVUGBJ.js';
|
|
16
20
|
import { ChatPage } from '../chunk-EWSEQEPL.js';
|
|
17
21
|
import '../chunk-KZJAG7T3.js';
|
|
18
22
|
import '../chunk-HOXKR26S.js';
|
|
19
23
|
import '../chunk-AA3PT6KR.js';
|
|
20
24
|
import '../chunk-NPLYHR2E.js';
|
|
21
25
|
import { initials } from '../chunk-OXF2IFXG.js';
|
|
26
|
+
import { PlainMailBodyEditor, draftMailCompose, emptyMailCompose, MailGrid, MailMessageView, replyMailCompose, MailDocumentPicker, parseMailRecipients } from '../chunk-LGX6UFYG.js';
|
|
22
27
|
import { ModalScaffold } from '../chunk-TX2ZICS7.js';
|
|
28
|
+
import '../chunk-ZPLG7QYF.js';
|
|
29
|
+
import '../chunk-SLSBJRWW.js';
|
|
23
30
|
import '../chunk-7F6CBP3Z.js';
|
|
24
31
|
import '../chunk-VUNV25KB.js';
|
|
25
32
|
import { lazy, useState, Suspense, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
26
33
|
import { QueryClient, QueryClientProvider, useQueryClient, useQuery } from '@tanstack/react-query';
|
|
27
|
-
import { ArbiProvider, useConversations, useRunConversation, useUploadSkillFiles, ArtifactCanvas, useArbiChat, useArbi, useDocuments,
|
|
34
|
+
import { ArbiProvider, useConversations, useRunConversation, useUploadSkillFiles, ArtifactCanvas, useArbiChat, useArbi, useDocuments, useTagKindSchema, useTags, useSetTagValues, useUpdateTagKindSchema, useInvalidateTagData, useWorkspaceUsers, useWorkspaces, useShellWorkspaces, useProfile, useUpdateProfile, useRequestEmailChange, useConfirmEmailChange, useNameEditor, useAvatarCrop, useEmailChange, ImageCropModal, useIdentityVerification, usePasswordChange, usePasswordChangeForm, useNotificationPreferences, useSessions, useCurrentSessionId, useTerminateSession, useLogoutAllSessions, useDeleteAccount, useDeleteAccountFlow, useArbiWebSocket, notify, useWorkNotifications, mailDraftIdFromContent, useMailClient, defaultMailAlias, useClaimNativeAlias, useMailOnline, useConnectMailAccount, useDisconnectMailAccount, useMailPolicy, useMailAccounts, useMailUnlock, useMailFolders, useMailMessages, useMailDraftCompose, useMailCache, useMailMessage, MailOfflineUnavailableError, accountPath, useAgentSession, useStartAgentSession, useCloseAgentSession, isAliasTakenError, nextAliasSuggestion, aliasTakenMessage, mailJson, ArbiWebSocketProvider, useCreateWork, useActOnStep, useConversationActivity, useConversationThreads, fileAttachment, useSaveMailDraft, useSendMail, AiAnswer } from '@arbidocs/react';
|
|
28
35
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
29
|
-
import { Button, Label, Input, cn } from '@arbidocs/react/ui';
|
|
36
|
+
import { Button, Label, Input, Dialog, DialogContent, DialogTitle, DialogDescription, Tooltip, TooltipTrigger, TooltipContent, cn } from '@arbidocs/react/ui';
|
|
30
37
|
import { useStreamedArtifact } from '@arbidocs/react/artifacts';
|
|
31
|
-
import { ExternalLink, UserCircle, KeyRound, Radio, Loader2, MessageSquare } from 'lucide-react';
|
|
32
|
-
import { useOptionalDocumentTools, DocumentToolsProvider } from '@arbidocs/react/document-tools';
|
|
38
|
+
import { ExternalLink, UserCircle, KeyRound, Radio, Mail, Settings, Plus, ArrowLeft, Loader2, X, Paperclip, Send, MessageSquare } from 'lucide-react';
|
|
39
|
+
import { useOptionalDocumentTools, fetchDocumentPdf, DocumentToolsProvider } from '@arbidocs/react/document-tools';
|
|
33
40
|
import { useNotify } from '@arbidocs/react/shell';
|
|
41
|
+
import { bytesToBase64 } from '@arbidocs/client';
|
|
34
42
|
|
|
35
43
|
function ConnectionBoundWebSocket({ children }) {
|
|
36
44
|
const { status } = useConnection();
|
|
@@ -292,6 +300,43 @@ function useWorkflowThread(workflow, options = {}) {
|
|
|
292
300
|
},
|
|
293
301
|
[actOnStep, refresh]
|
|
294
302
|
);
|
|
303
|
+
const createThread = useCallback(
|
|
304
|
+
async (values, done) => {
|
|
305
|
+
await ensureWorkspaceOpen();
|
|
306
|
+
const created = await createWork.mutateAsync({
|
|
307
|
+
title: runThreadTitle(workflow, values).slice(0, 60),
|
|
308
|
+
description: workflow.description || null,
|
|
309
|
+
prompt: null,
|
|
310
|
+
skill_ref: null,
|
|
311
|
+
skill_inputs: values,
|
|
312
|
+
agent_ext_id: options.agentId ?? null,
|
|
313
|
+
auth_mode: "run_now_temp",
|
|
314
|
+
plan: {
|
|
315
|
+
goal: "",
|
|
316
|
+
criteria: [],
|
|
317
|
+
max_passes: 3,
|
|
318
|
+
documents: [],
|
|
319
|
+
steps: toPlanSteps(workflow, {
|
|
320
|
+
inputs: values,
|
|
321
|
+
resolveWorkflow: options.resolveWorkflow,
|
|
322
|
+
compose: options.compose
|
|
323
|
+
})
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
const id = created.external_id;
|
|
327
|
+
setThreadId(id);
|
|
328
|
+
if (done) setLocalStates((s) => ({ ...s, [done]: { status: "done" } }));
|
|
329
|
+
options.onStarted?.(id);
|
|
330
|
+
for (const [stepId, st] of Object.entries(localStates))
|
|
331
|
+
if (st.status === "skipped")
|
|
332
|
+
await actOnStep.mutateAsync({
|
|
333
|
+
conversationId: id,
|
|
334
|
+
body: { step: stepId, action: "skip" }
|
|
335
|
+
});
|
|
336
|
+
return id;
|
|
337
|
+
},
|
|
338
|
+
[ensureWorkspaceOpen, createWork, workflow, options, localStates, actOnStep]
|
|
339
|
+
);
|
|
295
340
|
const complete = useCallback(
|
|
296
341
|
async (step) => {
|
|
297
342
|
let values = inputs;
|
|
@@ -301,34 +346,7 @@ function useWorkflowThread(workflow, options = {}) {
|
|
|
301
346
|
setLocalInputs(values);
|
|
302
347
|
setEdits({});
|
|
303
348
|
}
|
|
304
|
-
|
|
305
|
-
if (!id) {
|
|
306
|
-
await ensureWorkspaceOpen();
|
|
307
|
-
const created = await createWork.mutateAsync({
|
|
308
|
-
title: runThreadTitle(workflow, values).slice(0, 60),
|
|
309
|
-
description: workflow.description || null,
|
|
310
|
-
prompt: null,
|
|
311
|
-
skill_ref: null,
|
|
312
|
-
skill_inputs: values,
|
|
313
|
-
agent_ext_id: options.agentId ?? null,
|
|
314
|
-
auth_mode: "run_now_temp",
|
|
315
|
-
plan: {
|
|
316
|
-
goal: "",
|
|
317
|
-
criteria: [],
|
|
318
|
-
max_passes: 3,
|
|
319
|
-
documents: [],
|
|
320
|
-
steps: toPlanSteps(workflow, {
|
|
321
|
-
inputs: values,
|
|
322
|
-
resolveWorkflow: options.resolveWorkflow,
|
|
323
|
-
compose: options.compose
|
|
324
|
-
})
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
id = created.external_id;
|
|
328
|
-
setThreadId(id);
|
|
329
|
-
setLocalStates((s) => ({ ...s, [step.id]: { status: "done" } }));
|
|
330
|
-
options.onStarted?.(id);
|
|
331
|
-
}
|
|
349
|
+
const id = threadId ?? await createThread(values, step.id);
|
|
332
350
|
const documents = step.kind === "upload" ? options.documentsFor?.(step.id) : void 0;
|
|
333
351
|
await act(id, {
|
|
334
352
|
step: step.id,
|
|
@@ -337,8 +355,17 @@ function useWorkflowThread(workflow, options = {}) {
|
|
|
337
355
|
...documents?.length ? { documents } : {}
|
|
338
356
|
});
|
|
339
357
|
},
|
|
340
|
-
[inputs, options, localStates, threadId,
|
|
358
|
+
[inputs, options, localStates, threadId, createThread, act]
|
|
341
359
|
);
|
|
360
|
+
const start = useCallback(async () => {
|
|
361
|
+
if (!threadId) {
|
|
362
|
+
await createThread(inputs);
|
|
363
|
+
await refresh();
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
await arbi.conversations.runWithAuth(threadId);
|
|
367
|
+
await refresh();
|
|
368
|
+
}, [threadId, createThread, inputs, arbi, refresh]);
|
|
342
369
|
const skip = useCallback(
|
|
343
370
|
async (stepId) => {
|
|
344
371
|
if (!threadId) {
|
|
@@ -372,6 +399,10 @@ function useWorkflowThread(workflow, options = {}) {
|
|
|
372
399
|
[threadId, act]
|
|
373
400
|
);
|
|
374
401
|
const loaded = !options.conversationId || activity.isSuccess || activity.isError;
|
|
402
|
+
const idle = !!conversation && !conversation.in_progress;
|
|
403
|
+
const anyRunning = Object.values(states).some((s) => s.status === "running");
|
|
404
|
+
const needsStart = !threadId || idle && !anyRunning;
|
|
405
|
+
const runError = idle ? conversation?.last_error ?? null : null;
|
|
375
406
|
const view = useWorkflowView({
|
|
376
407
|
steps,
|
|
377
408
|
states,
|
|
@@ -380,6 +411,9 @@ function useWorkflowThread(workflow, options = {}) {
|
|
|
380
411
|
complete,
|
|
381
412
|
skip,
|
|
382
413
|
retry,
|
|
414
|
+
start,
|
|
415
|
+
needsStart,
|
|
416
|
+
runError,
|
|
383
417
|
sendBack,
|
|
384
418
|
decline,
|
|
385
419
|
nameOf,
|
|
@@ -429,6 +463,7 @@ function WorkflowRunThread({
|
|
|
429
463
|
}
|
|
430
464
|
var DEFAULT_WORKFLOW_RUNNER_DIALOG_LABELS = {
|
|
431
465
|
loadingDocument: "Opening the document\u2026",
|
|
466
|
+
preparingDocument: "The PDF is still being prepared\u2026",
|
|
432
467
|
documentFailed: "The document could not be opened.",
|
|
433
468
|
reviewTitle: "Review",
|
|
434
469
|
reviewDescription: "Each document against each review column.",
|
|
@@ -436,6 +471,9 @@ var DEFAULT_WORKFLOW_RUNNER_DIALOG_LABELS = {
|
|
|
436
471
|
empty: "\u2014",
|
|
437
472
|
wordTitle: "Tracked changes"
|
|
438
473
|
};
|
|
474
|
+
var SIGN_PDF_RETRIES = 4;
|
|
475
|
+
var SIGN_PDF_RETRY_DELAY_MS = 1500;
|
|
476
|
+
var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
439
477
|
function WorkflowSignDialog({
|
|
440
478
|
docRef,
|
|
441
479
|
onClose,
|
|
@@ -444,18 +482,33 @@ function WorkflowSignDialog({
|
|
|
444
482
|
const arbi = useArbi();
|
|
445
483
|
const { user, ensureWorkspaceOpen } = useConnection();
|
|
446
484
|
const tools = useOptionalDocumentTools();
|
|
447
|
-
const { id, version } = splitDocRef(docRef);
|
|
485
|
+
const { id, version: stepVersion } = splitDocRef(docRef);
|
|
486
|
+
const [version, setVersion] = useState(stepVersion);
|
|
448
487
|
const [pdf, setPdf] = useState(null);
|
|
488
|
+
const [preparing, setPreparing] = useState(false);
|
|
449
489
|
const [failed, setFailed] = useState(false);
|
|
450
490
|
useEffect(() => {
|
|
451
491
|
let live = true;
|
|
492
|
+
setPdf(null);
|
|
493
|
+
setFailed(false);
|
|
494
|
+
setPreparing(false);
|
|
452
495
|
void (async () => {
|
|
453
496
|
try {
|
|
454
497
|
await ensureWorkspaceOpen();
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
498
|
+
const client = arbi.getClient().fetch;
|
|
499
|
+
const ref = version ? `${id}@v${version}` : id;
|
|
500
|
+
for (let attempt = 0; ; attempt++) {
|
|
501
|
+
try {
|
|
502
|
+
const blob = await fetchDocumentPdf(client, ref);
|
|
503
|
+
if (live) setPdf(blob);
|
|
504
|
+
return;
|
|
505
|
+
} catch (err) {
|
|
506
|
+
if (!live || attempt >= SIGN_PDF_RETRIES) throw err;
|
|
507
|
+
setPreparing(true);
|
|
508
|
+
await wait(SIGN_PDF_RETRY_DELAY_MS);
|
|
509
|
+
if (!live) return;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
459
512
|
} catch {
|
|
460
513
|
if (live) setFailed(true);
|
|
461
514
|
}
|
|
@@ -476,11 +529,22 @@ function WorkflowSignDialog({
|
|
|
476
529
|
children: /* @__PURE__ */ jsx(ModalScaffold.Body, { children: /* @__PURE__ */ jsx("p", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: failed ? labels.documentFailed : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
477
530
|
/* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
|
|
478
531
|
" ",
|
|
479
|
-
labels.loadingDocument
|
|
532
|
+
preparing ? labels.preparingDocument : labels.loadingDocument
|
|
480
533
|
] }) }) })
|
|
481
534
|
}
|
|
482
535
|
);
|
|
483
|
-
const dialog = /* @__PURE__ */ jsx(
|
|
536
|
+
const dialog = /* @__PURE__ */ jsx(
|
|
537
|
+
SignDocumentDialog,
|
|
538
|
+
{
|
|
539
|
+
open: true,
|
|
540
|
+
onOpenChange: (o) => !o && onClose(),
|
|
541
|
+
documentId: id,
|
|
542
|
+
pdfBlob: pdf,
|
|
543
|
+
version,
|
|
544
|
+
onOpenCurrent: () => setVersion(void 0)
|
|
545
|
+
},
|
|
546
|
+
version ?? "current"
|
|
547
|
+
);
|
|
484
548
|
return tools ? dialog : /* @__PURE__ */ jsx(
|
|
485
549
|
DocumentToolsProvider,
|
|
486
550
|
{
|
|
@@ -958,7 +1022,17 @@ function ReviewNotify({
|
|
|
958
1022
|
] })
|
|
959
1023
|
] });
|
|
960
1024
|
}
|
|
961
|
-
|
|
1025
|
+
var FLAT_KINDS = ["issue", "person", "organisation"];
|
|
1026
|
+
var KIND_TITLES = {
|
|
1027
|
+
label: "Labels",
|
|
1028
|
+
annotation: "Annotations",
|
|
1029
|
+
search: "Searches",
|
|
1030
|
+
issue: "Issues",
|
|
1031
|
+
person: "People",
|
|
1032
|
+
organisation: "Organisations"
|
|
1033
|
+
};
|
|
1034
|
+
function ConnectedTagTable({
|
|
1035
|
+
kind,
|
|
962
1036
|
parentId,
|
|
963
1037
|
linksLimit = 3,
|
|
964
1038
|
exportTitle,
|
|
@@ -969,8 +1043,16 @@ function ConnectedTagMatrix({
|
|
|
969
1043
|
}) {
|
|
970
1044
|
const arbi = useArbi();
|
|
971
1045
|
const { workspaceId, status, ensureWorkspaceOpen } = useConnection();
|
|
972
|
-
const
|
|
973
|
-
const
|
|
1046
|
+
const schema = useTagKindSchema(kind);
|
|
1047
|
+
const rows = useTags(workspaceId ?? void 0, {
|
|
1048
|
+
kind,
|
|
1049
|
+
...parentId ? { parentId } : {},
|
|
1050
|
+
include: ["links"],
|
|
1051
|
+
linksLimit
|
|
1052
|
+
});
|
|
1053
|
+
const canAddRows = !readOnly && kind !== "annotation" && kind !== "search";
|
|
1054
|
+
const setValues = useSetTagValues();
|
|
1055
|
+
const updateSchema = useUpdateTagKindSchema();
|
|
974
1056
|
const invalidate = useInvalidateTagData();
|
|
975
1057
|
const members = useWorkspaceUsers(workspaceId ?? void 0, {
|
|
976
1058
|
enabled: status === "authenticated" && !!workspaceId,
|
|
@@ -981,10 +1063,14 @@ function ConnectedTagMatrix({
|
|
|
981
1063
|
value: m.user.external_id,
|
|
982
1064
|
label: [m.user.given_name, m.user.family_name].filter(Boolean).join(" ") || m.user.email
|
|
983
1065
|
}));
|
|
1066
|
+
const table = useMemo(
|
|
1067
|
+
() => schema.data && rows.data ? { columns: schema.data.fields, rows: rows.data } : void 0,
|
|
1068
|
+
[schema.data, rows.data]
|
|
1069
|
+
);
|
|
984
1070
|
const fail = (e) => setError(e instanceof Error ? e.message : String(e));
|
|
985
1071
|
const snapshot = (data) => {
|
|
986
|
-
const title = exportTitle ??
|
|
987
|
-
const bytes =
|
|
1072
|
+
const title = exportTitle ?? KIND_TITLES[kind];
|
|
1073
|
+
const bytes = tagTableDocx(data, {
|
|
988
1074
|
title,
|
|
989
1075
|
nameOf: (id) => people.find((p) => p.value === id)?.label
|
|
990
1076
|
});
|
|
@@ -994,32 +1080,42 @@ function ConnectedTagMatrix({
|
|
|
994
1080
|
};
|
|
995
1081
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
996
1082
|
/* @__PURE__ */ jsx(
|
|
997
|
-
|
|
1083
|
+
TagTable,
|
|
998
1084
|
{
|
|
999
1085
|
...props,
|
|
1000
|
-
|
|
1001
|
-
isLoading:
|
|
1086
|
+
table,
|
|
1087
|
+
isLoading: schema.isLoading || rows.isLoading,
|
|
1002
1088
|
people,
|
|
1003
1089
|
onChangeValue: readOnly ? void 0 : async (tagId, fieldId, value) => {
|
|
1004
1090
|
setError(null);
|
|
1005
|
-
await
|
|
1091
|
+
await setValues.mutateAsync({ tagId, values: { [fieldId]: value } }).catch(fail);
|
|
1006
1092
|
},
|
|
1007
|
-
|
|
1093
|
+
onChangeColumns: readOnly ? void 0 : async (fields) => {
|
|
1094
|
+
setError(null);
|
|
1095
|
+
await ensureWorkspaceOpen();
|
|
1096
|
+
await updateSchema.mutateAsync({ kind, fields }).catch((e) => {
|
|
1097
|
+
fail(e);
|
|
1098
|
+
throw e;
|
|
1099
|
+
});
|
|
1100
|
+
},
|
|
1101
|
+
onAddRow: !canAddRows ? void 0 : async (name) => {
|
|
1008
1102
|
setError(null);
|
|
1009
1103
|
try {
|
|
1010
1104
|
await ensureWorkspaceOpen();
|
|
1011
|
-
await arbi.tags.create({
|
|
1105
|
+
await arbi.tags.create({
|
|
1106
|
+
name,
|
|
1107
|
+
kind,
|
|
1108
|
+
...parentId && !FLAT_KINDS.includes(kind) ? { parentId } : {}
|
|
1109
|
+
});
|
|
1012
1110
|
await invalidate();
|
|
1013
1111
|
} catch (e) {
|
|
1014
1112
|
fail(e);
|
|
1015
1113
|
}
|
|
1016
1114
|
},
|
|
1017
|
-
onExport: onExported &&
|
|
1115
|
+
onExport: onExported && table ? async () => {
|
|
1018
1116
|
setError(null);
|
|
1019
1117
|
try {
|
|
1020
|
-
const
|
|
1021
|
-
if (!data) return;
|
|
1022
|
-
const { blob, name } = snapshot(data);
|
|
1118
|
+
const { blob, name } = snapshot(table);
|
|
1023
1119
|
await ensureWorkspaceOpen();
|
|
1024
1120
|
const uploaded = await arbi.documents.uploadFile(blob, name);
|
|
1025
1121
|
const id = uploaded?.doc_ext_ids?.[0];
|
|
@@ -1028,12 +1124,10 @@ function ConnectedTagMatrix({
|
|
|
1028
1124
|
fail(e);
|
|
1029
1125
|
}
|
|
1030
1126
|
} : void 0,
|
|
1031
|
-
onDownload: downloadable &&
|
|
1127
|
+
onDownload: downloadable && table ? () => {
|
|
1032
1128
|
setError(null);
|
|
1033
1129
|
try {
|
|
1034
|
-
const
|
|
1035
|
-
if (!data) return;
|
|
1036
|
-
const { blob, name } = snapshot(data);
|
|
1130
|
+
const { blob, name } = snapshot(table);
|
|
1037
1131
|
const url = URL.createObjectURL(blob);
|
|
1038
1132
|
const link = document.createElement("a");
|
|
1039
1133
|
link.href = url;
|
|
@@ -1567,6 +1661,14 @@ function mapWorkNotification(note) {
|
|
|
1567
1661
|
return null;
|
|
1568
1662
|
}
|
|
1569
1663
|
}
|
|
1664
|
+
if (note.type === "mail_draft_ready") {
|
|
1665
|
+
const draftId = mailDraftIdFromContent(note.content);
|
|
1666
|
+
if (!draftId) return null;
|
|
1667
|
+
return {
|
|
1668
|
+
title: `${sender} left a draft email for you to review`,
|
|
1669
|
+
target: { kind: "mail_draft", draftId, mailbox: "native" }
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1570
1672
|
const titles = {
|
|
1571
1673
|
mention: `${sender} mentioned you in a conversation`,
|
|
1572
1674
|
workspace_access_request: `${sender} needs workspace access to run a task`,
|
|
@@ -1586,6 +1688,1510 @@ function mapWorkNotification(note) {
|
|
|
1586
1688
|
}
|
|
1587
1689
|
};
|
|
1588
1690
|
}
|
|
1691
|
+
function attachmentPath(accountId, messageId, attachmentId) {
|
|
1692
|
+
return `${accountPath(accountId)}/messages/${encodeURIComponent(messageId)}/attachments/${encodeURIComponent(attachmentId)}`;
|
|
1693
|
+
}
|
|
1694
|
+
function saveFile(blob, name) {
|
|
1695
|
+
const url = URL.createObjectURL(blob);
|
|
1696
|
+
const link = document.createElement("a");
|
|
1697
|
+
link.href = url;
|
|
1698
|
+
link.download = name;
|
|
1699
|
+
link.click();
|
|
1700
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
1701
|
+
}
|
|
1702
|
+
function hydrateMailAttachments(client, account, messageId, attachments, { isOnline, offline }) {
|
|
1703
|
+
return Promise.all(
|
|
1704
|
+
attachments.map(async (a) => {
|
|
1705
|
+
if (a.content_base64 != null) return a;
|
|
1706
|
+
if (!isOnline) {
|
|
1707
|
+
const file = await offline?.loadAttachment(account.id, messageId, a);
|
|
1708
|
+
return file ? { ...a, content_base64: bytesToBase64(new Uint8Array(await file.arrayBuffer())) } : { ...a, content_base64: null };
|
|
1709
|
+
}
|
|
1710
|
+
const response = await client.fetch(attachmentPath(account.id, messageId, a.id));
|
|
1711
|
+
return fileAttachment(new File([await response.blob()], a.name, { type: a.content_type }));
|
|
1712
|
+
})
|
|
1713
|
+
);
|
|
1714
|
+
}
|
|
1715
|
+
function ConnectedMailAccounts({
|
|
1716
|
+
policy,
|
|
1717
|
+
accounts,
|
|
1718
|
+
onClose,
|
|
1719
|
+
embedded = false,
|
|
1720
|
+
user,
|
|
1721
|
+
oauthReturnPath,
|
|
1722
|
+
settings
|
|
1723
|
+
}) {
|
|
1724
|
+
const { client } = useMailClient();
|
|
1725
|
+
const [profileId, setProfileId] = useState(policy.server_profiles[0]?.id ?? "");
|
|
1726
|
+
const [email, setEmail] = useState("");
|
|
1727
|
+
const [username, setUsername] = useState("");
|
|
1728
|
+
const [password, setPassword] = useState("");
|
|
1729
|
+
const [error, setError] = useState("");
|
|
1730
|
+
const [alias, setAlias] = useState(() => defaultMailAlias(user));
|
|
1731
|
+
const native = useClaimNativeAlias(onClose);
|
|
1732
|
+
const isOnline = useMailOnline();
|
|
1733
|
+
const offline = !isOnline;
|
|
1734
|
+
const onNativeError = (err) => {
|
|
1735
|
+
if (!isAliasTakenError(err)) return setError(err.message);
|
|
1736
|
+
const suggestion = nextAliasSuggestion(alias);
|
|
1737
|
+
setAlias(suggestion);
|
|
1738
|
+
setError(aliasTakenMessage(alias, suggestion, policy.native_domain));
|
|
1739
|
+
};
|
|
1740
|
+
const connect = useConnectMailAccount();
|
|
1741
|
+
const disconnect = useDisconnectMailAccount();
|
|
1742
|
+
const authorizeOAuth = client.authorizeOAuth;
|
|
1743
|
+
const oauth = async (vendor) => {
|
|
1744
|
+
if (!authorizeOAuth) return;
|
|
1745
|
+
try {
|
|
1746
|
+
window.location.assign(
|
|
1747
|
+
await authorizeOAuth(
|
|
1748
|
+
vendor,
|
|
1749
|
+
oauthReturnPath ?? `${window.location.pathname}${window.location.search}`
|
|
1750
|
+
)
|
|
1751
|
+
);
|
|
1752
|
+
} catch (err) {
|
|
1753
|
+
setError(err instanceof Error ? err.message : "Could not connect account");
|
|
1754
|
+
}
|
|
1755
|
+
};
|
|
1756
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1757
|
+
offline && /* @__PURE__ */ jsx(
|
|
1758
|
+
"p",
|
|
1759
|
+
{
|
|
1760
|
+
role: "status",
|
|
1761
|
+
className: "mb-4 rounded bg-muted/40 px-3 py-2 text-sm text-muted-foreground",
|
|
1762
|
+
children: "You're offline. Creating, connecting or removing accounts needs a connection."
|
|
1763
|
+
}
|
|
1764
|
+
),
|
|
1765
|
+
policy.native_enabled && !accounts.some((a) => a.provider === "native") && /* @__PURE__ */ jsxs(
|
|
1766
|
+
"form",
|
|
1767
|
+
{
|
|
1768
|
+
className: "mb-5 grid gap-3",
|
|
1769
|
+
onSubmit: (e) => {
|
|
1770
|
+
e.preventDefault();
|
|
1771
|
+
native.mutate(alias, { onError: onNativeError });
|
|
1772
|
+
},
|
|
1773
|
+
children: [
|
|
1774
|
+
/* @__PURE__ */ jsxs("label", { className: "grid gap-1 text-sm", children: [
|
|
1775
|
+
"Choose your email address",
|
|
1776
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1777
|
+
/* @__PURE__ */ jsx(
|
|
1778
|
+
"input",
|
|
1779
|
+
{
|
|
1780
|
+
"aria-label": "Email alias",
|
|
1781
|
+
required: true,
|
|
1782
|
+
pattern: "[a-z0-9][a-z0-9._\\-]{0,62}",
|
|
1783
|
+
className: "min-w-0 flex-1 rounded border bg-background p-2",
|
|
1784
|
+
value: alias,
|
|
1785
|
+
onChange: (e) => setAlias(e.target.value.toLowerCase())
|
|
1786
|
+
}
|
|
1787
|
+
),
|
|
1788
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1789
|
+
"@",
|
|
1790
|
+
policy.native_domain
|
|
1791
|
+
] })
|
|
1792
|
+
] })
|
|
1793
|
+
] }),
|
|
1794
|
+
/* @__PURE__ */ jsx(Button, { disabled: native.isPending || offline, type: "submit", children: "Create inbox" })
|
|
1795
|
+
]
|
|
1796
|
+
}
|
|
1797
|
+
),
|
|
1798
|
+
/* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: "Connect an account using the providers enabled by your administrator." }),
|
|
1799
|
+
authorizeOAuth && /* @__PURE__ */ jsx("div", { className: "mb-4 flex flex-wrap gap-2", children: policy.oauth_providers.map((vendor) => /* @__PURE__ */ jsxs(
|
|
1800
|
+
Button,
|
|
1801
|
+
{
|
|
1802
|
+
variant: "outline",
|
|
1803
|
+
disabled: offline,
|
|
1804
|
+
onClick: () => void oauth(vendor),
|
|
1805
|
+
children: [
|
|
1806
|
+
"Connect ",
|
|
1807
|
+
vendor === "microsoft" ? "Outlook" : "Gmail"
|
|
1808
|
+
]
|
|
1809
|
+
},
|
|
1810
|
+
vendor
|
|
1811
|
+
)) }),
|
|
1812
|
+
accounts.map((account) => /* @__PURE__ */ jsxs(
|
|
1813
|
+
"div",
|
|
1814
|
+
{
|
|
1815
|
+
className: "mb-2 flex items-center justify-between gap-2 rounded border p-3 text-sm",
|
|
1816
|
+
children: [
|
|
1817
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1818
|
+
/* @__PURE__ */ jsx("div", { children: account.email }),
|
|
1819
|
+
/* @__PURE__ */ jsx("div", { className: "text-muted-foreground", children: account.display_name })
|
|
1820
|
+
] }),
|
|
1821
|
+
account.profile_id ? /* @__PURE__ */ jsx(
|
|
1822
|
+
Button,
|
|
1823
|
+
{
|
|
1824
|
+
variant: "ghost",
|
|
1825
|
+
disabled: disconnect.isPending || offline,
|
|
1826
|
+
onClick: () => disconnect.mutate(account.id, { onError: (err) => setError(err.message) }),
|
|
1827
|
+
children: "Disconnect"
|
|
1828
|
+
}
|
|
1829
|
+
) : account.provider !== "native" ? authorizeOAuth && /* @__PURE__ */ jsx(
|
|
1830
|
+
Button,
|
|
1831
|
+
{
|
|
1832
|
+
variant: "ghost",
|
|
1833
|
+
disabled: offline,
|
|
1834
|
+
onClick: () => void oauth(account.provider),
|
|
1835
|
+
children: "Reconnect"
|
|
1836
|
+
}
|
|
1837
|
+
) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Native inbox" })
|
|
1838
|
+
]
|
|
1839
|
+
},
|
|
1840
|
+
account.id
|
|
1841
|
+
)),
|
|
1842
|
+
policy.server_profiles.length > 0 && /* @__PURE__ */ jsxs(
|
|
1843
|
+
"form",
|
|
1844
|
+
{
|
|
1845
|
+
className: "mt-5 grid gap-3",
|
|
1846
|
+
onSubmit: (event) => {
|
|
1847
|
+
event.preventDefault();
|
|
1848
|
+
setError("");
|
|
1849
|
+
connect.mutate(
|
|
1850
|
+
{ profileId, email, username, password },
|
|
1851
|
+
{
|
|
1852
|
+
onSuccess: () => {
|
|
1853
|
+
setPassword("");
|
|
1854
|
+
onClose();
|
|
1855
|
+
},
|
|
1856
|
+
onError: (err) => setError(err.message)
|
|
1857
|
+
}
|
|
1858
|
+
);
|
|
1859
|
+
},
|
|
1860
|
+
children: [
|
|
1861
|
+
/* @__PURE__ */ jsxs("label", { className: "grid gap-1 text-sm", children: [
|
|
1862
|
+
"Mail server",
|
|
1863
|
+
/* @__PURE__ */ jsx(
|
|
1864
|
+
"select",
|
|
1865
|
+
{
|
|
1866
|
+
"aria-label": "Mail server",
|
|
1867
|
+
className: "rounded border bg-background p-2",
|
|
1868
|
+
value: profileId,
|
|
1869
|
+
onChange: (e) => setProfileId(e.target.value),
|
|
1870
|
+
children: policy.server_profiles.map((profile) => /* @__PURE__ */ jsx("option", { value: profile.id, children: profile.name }, profile.id))
|
|
1871
|
+
}
|
|
1872
|
+
)
|
|
1873
|
+
] }),
|
|
1874
|
+
/* @__PURE__ */ jsxs("label", { className: "grid gap-1 text-sm", children: [
|
|
1875
|
+
"Email address",
|
|
1876
|
+
/* @__PURE__ */ jsx(
|
|
1877
|
+
"input",
|
|
1878
|
+
{
|
|
1879
|
+
required: true,
|
|
1880
|
+
type: "email",
|
|
1881
|
+
autoComplete: "email",
|
|
1882
|
+
className: "rounded border bg-background p-2",
|
|
1883
|
+
value: email,
|
|
1884
|
+
onChange: (e) => setEmail(e.target.value)
|
|
1885
|
+
}
|
|
1886
|
+
)
|
|
1887
|
+
] }),
|
|
1888
|
+
/* @__PURE__ */ jsxs("label", { className: "grid gap-1 text-sm", children: [
|
|
1889
|
+
"Username",
|
|
1890
|
+
/* @__PURE__ */ jsx(
|
|
1891
|
+
"input",
|
|
1892
|
+
{
|
|
1893
|
+
placeholder: "Same as email address",
|
|
1894
|
+
autoComplete: "username",
|
|
1895
|
+
className: "rounded border bg-background p-2",
|
|
1896
|
+
value: username,
|
|
1897
|
+
onChange: (e) => setUsername(e.target.value)
|
|
1898
|
+
}
|
|
1899
|
+
)
|
|
1900
|
+
] }),
|
|
1901
|
+
/* @__PURE__ */ jsxs("label", { className: "grid gap-1 text-sm", children: [
|
|
1902
|
+
"Password or app password",
|
|
1903
|
+
/* @__PURE__ */ jsx(
|
|
1904
|
+
"input",
|
|
1905
|
+
{
|
|
1906
|
+
required: true,
|
|
1907
|
+
type: "password",
|
|
1908
|
+
autoComplete: "current-password",
|
|
1909
|
+
className: "rounded border bg-background p-2",
|
|
1910
|
+
value: password,
|
|
1911
|
+
onChange: (e) => setPassword(e.target.value)
|
|
1912
|
+
}
|
|
1913
|
+
)
|
|
1914
|
+
] }),
|
|
1915
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: connect.isPending || offline, children: connect.isPending ? "Connecting\u2026" : "Connect mailbox" })
|
|
1916
|
+
]
|
|
1917
|
+
}
|
|
1918
|
+
),
|
|
1919
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-3 text-sm text-destructive", children: error }),
|
|
1920
|
+
accounts.length > 0 && settings
|
|
1921
|
+
] });
|
|
1922
|
+
if (embedded) return /* @__PURE__ */ jsx("div", { className: "max-w-2xl", children: content });
|
|
1923
|
+
return /* @__PURE__ */ jsx(
|
|
1924
|
+
Dialog,
|
|
1925
|
+
{
|
|
1926
|
+
open: true,
|
|
1927
|
+
onOpenChange: (open) => {
|
|
1928
|
+
if (!open) onClose();
|
|
1929
|
+
},
|
|
1930
|
+
children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[90dvh] max-w-lg overflow-auto", children: [
|
|
1931
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Mail accounts" }),
|
|
1932
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Choose your email address or connect an external mailbox." }),
|
|
1933
|
+
content
|
|
1934
|
+
] })
|
|
1935
|
+
}
|
|
1936
|
+
);
|
|
1937
|
+
}
|
|
1938
|
+
var MailBodyEditor = lazy(
|
|
1939
|
+
() => import('../MailBodyEditor-5PIQLYVY.js').catch(() => ({
|
|
1940
|
+
default: PlainMailBodyEditor
|
|
1941
|
+
}))
|
|
1942
|
+
);
|
|
1943
|
+
function MailComposerForm({
|
|
1944
|
+
account,
|
|
1945
|
+
initial,
|
|
1946
|
+
maxBytes,
|
|
1947
|
+
ready,
|
|
1948
|
+
drafts,
|
|
1949
|
+
onClose,
|
|
1950
|
+
onOpenDraft,
|
|
1951
|
+
onChange,
|
|
1952
|
+
embedded = false,
|
|
1953
|
+
closeRequestRef,
|
|
1954
|
+
localDraftId: initialLocalDraftId,
|
|
1955
|
+
offline,
|
|
1956
|
+
documents,
|
|
1957
|
+
notify: notify3
|
|
1958
|
+
}) {
|
|
1959
|
+
const { client } = useMailClient();
|
|
1960
|
+
const [to, setTo] = useState(initial.to.map((r) => r.email).join("; "));
|
|
1961
|
+
const [cc, setCc] = useState(initial.cc.map((r) => r.email).join("; "));
|
|
1962
|
+
const [showCc, setShowCc] = useState(initial.cc.length > 0);
|
|
1963
|
+
const [showBcc, setShowBcc] = useState(initial.bcc.length > 0);
|
|
1964
|
+
const [bcc, setBcc] = useState(initial.bcc.map((r) => r.email).join("; "));
|
|
1965
|
+
const [subject, setSubject] = useState(initial.subject);
|
|
1966
|
+
const [text, setText] = useState(initial.text);
|
|
1967
|
+
const [html, setHtml] = useState(initial.html);
|
|
1968
|
+
const [attachments, setAttachments] = useState(initial.attachments);
|
|
1969
|
+
const [draftId, setDraftId] = useState(initial.draft_id);
|
|
1970
|
+
const [error, setError] = useState("");
|
|
1971
|
+
const [dirty, setDirty] = useState(false);
|
|
1972
|
+
const [attaching, setAttaching] = useState(false);
|
|
1973
|
+
const [saved, setSaved] = useState(false);
|
|
1974
|
+
const isOnline = useMailOnline();
|
|
1975
|
+
const [localDraftId, setLocalDraftId] = useState(initialLocalDraftId);
|
|
1976
|
+
const [savedLocally, setSavedLocally] = useState(false);
|
|
1977
|
+
const [secure, setSecure] = useState(initial.secure_delivery ?? false);
|
|
1978
|
+
const securePayload = useRef(null);
|
|
1979
|
+
const requestId = useRef(crypto.randomUUID());
|
|
1980
|
+
const attemptedPayload = useRef(null);
|
|
1981
|
+
const fileInput = useRef(null);
|
|
1982
|
+
const sameSubjectDraft = useMemo(() => {
|
|
1983
|
+
const key = subject.trim().toLowerCase();
|
|
1984
|
+
return key ? drafts.find((d) => d.id !== draftId && d.subject.trim().toLowerCase() === key) : void 0;
|
|
1985
|
+
}, [drafts, draftId, subject]);
|
|
1986
|
+
const body = () => ({
|
|
1987
|
+
...initial,
|
|
1988
|
+
to: parseMailRecipients(to),
|
|
1989
|
+
cc: parseMailRecipients(cc),
|
|
1990
|
+
bcc: parseMailRecipients(bcc),
|
|
1991
|
+
subject,
|
|
1992
|
+
text,
|
|
1993
|
+
html,
|
|
1994
|
+
attachments,
|
|
1995
|
+
draft_id: draftId,
|
|
1996
|
+
secure_delivery: secure
|
|
1997
|
+
});
|
|
1998
|
+
const saveOnDevice = async (compose) => {
|
|
1999
|
+
const draft = await offline.saveDraft(account.id, compose, localDraftId);
|
|
2000
|
+
setLocalDraftId(draft.id);
|
|
2001
|
+
};
|
|
2002
|
+
const save = useSaveMailDraft(
|
|
2003
|
+
offline && { keep: saveOnDevice, isNetworkFailure: offline.isNetworkFailure }
|
|
2004
|
+
);
|
|
2005
|
+
const queueInOutbox = async (compose) => {
|
|
2006
|
+
await offline.queueSend({
|
|
2007
|
+
account,
|
|
2008
|
+
compose,
|
|
2009
|
+
requestId: requestId.current,
|
|
2010
|
+
secure,
|
|
2011
|
+
securePayload: securePayload.current ?? void 0
|
|
2012
|
+
});
|
|
2013
|
+
if (localDraftId) await offline.deleteDraft(localDraftId);
|
|
2014
|
+
};
|
|
2015
|
+
const send = useSendMail(
|
|
2016
|
+
offline && { keep: queueInOutbox, isNetworkFailure: offline.isNetworkFailure }
|
|
2017
|
+
);
|
|
2018
|
+
const saveDraft = () => save.mutate(
|
|
2019
|
+
{ accountId: account.id, compose: body() },
|
|
2020
|
+
{
|
|
2021
|
+
onSuccess: (result) => {
|
|
2022
|
+
setSaved(true);
|
|
2023
|
+
setDirty(false);
|
|
2024
|
+
if ("onDevice" in result) {
|
|
2025
|
+
setSavedLocally(true);
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
setSavedLocally(false);
|
|
2029
|
+
setDraftId(result.id);
|
|
2030
|
+
if (localDraftId) {
|
|
2031
|
+
void offline?.deleteDraft(localDraftId);
|
|
2032
|
+
setLocalDraftId(void 0);
|
|
2033
|
+
}
|
|
2034
|
+
},
|
|
2035
|
+
onError: (err) => setError(err.message)
|
|
2036
|
+
}
|
|
2037
|
+
);
|
|
2038
|
+
const sendMessage = () => {
|
|
2039
|
+
let value = attemptedPayload.current;
|
|
2040
|
+
if (value === null) {
|
|
2041
|
+
value = body();
|
|
2042
|
+
const recipients = [...value.to, ...value.cc, ...value.bcc];
|
|
2043
|
+
const problem = !recipients.length || recipients.length > 100 ? "Enter between 1 and 100 recipients" : recipients.some((r) => !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(r.email)) ? "Check the recipient email addresses" : value.attachments.reduce((total, a) => total + a.size, 0) > maxBytes ? "Attachments exceed the email size limit" : null;
|
|
2044
|
+
if (problem) return setError(problem);
|
|
2045
|
+
if (!(offline && !isOnline)) attemptedPayload.current = value;
|
|
2046
|
+
}
|
|
2047
|
+
const sending = account;
|
|
2048
|
+
send.mutate(
|
|
2049
|
+
{
|
|
2050
|
+
accountId: account.id,
|
|
2051
|
+
compose: value,
|
|
2052
|
+
requestId: requestId.current,
|
|
2053
|
+
// Secure delivery prepares its encrypted payload once.
|
|
2054
|
+
secure: secure ? async () => {
|
|
2055
|
+
if (sending?.provider !== "native" || !client.prepareSecureMail)
|
|
2056
|
+
throw new Error("Choose your native inbox to send securely");
|
|
2057
|
+
securePayload.current ??= await client.prepareSecureMail(
|
|
2058
|
+
value,
|
|
2059
|
+
sending,
|
|
2060
|
+
requestId.current
|
|
2061
|
+
);
|
|
2062
|
+
return securePayload.current;
|
|
2063
|
+
} : void 0
|
|
2064
|
+
},
|
|
2065
|
+
{
|
|
2066
|
+
onSuccess: (result) => {
|
|
2067
|
+
if (result && typeof result === "object" && "onDevice" in result) {
|
|
2068
|
+
notify3("Saved to your Outbox", "info", {
|
|
2069
|
+
description: "It will be sent automatically when you're back online."
|
|
2070
|
+
});
|
|
2071
|
+
onClose();
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
notify3("Email queued for delivery", "success");
|
|
2075
|
+
if (localDraftId) void offline?.deleteDraft(localDraftId);
|
|
2076
|
+
onClose();
|
|
2077
|
+
},
|
|
2078
|
+
onError: (err) => setError(err.message)
|
|
2079
|
+
}
|
|
2080
|
+
);
|
|
2081
|
+
};
|
|
2082
|
+
const busy = attaching || save.isPending || send.isPending;
|
|
2083
|
+
const locked = busy || attemptedPayload.current !== null;
|
|
2084
|
+
const addFiles = async (files) => {
|
|
2085
|
+
if (!files) return;
|
|
2086
|
+
const selected = Array.from(files);
|
|
2087
|
+
if (attachments.reduce((n, a) => n + a.size, 0) + selected.reduce((n, f) => n + f.size, 0) > maxBytes) {
|
|
2088
|
+
setError(`Attachments must total less than ${Math.round(maxBytes / 1024 / 1024)} MB`);
|
|
2089
|
+
return;
|
|
2090
|
+
}
|
|
2091
|
+
setAttaching(true);
|
|
2092
|
+
try {
|
|
2093
|
+
const added = await Promise.all(selected.map(fileAttachment));
|
|
2094
|
+
setAttachments((previous) => [...previous, ...added]);
|
|
2095
|
+
setSaved(false);
|
|
2096
|
+
setDirty(true);
|
|
2097
|
+
} catch (err) {
|
|
2098
|
+
setError(err instanceof Error ? err.message : "Could not attach files");
|
|
2099
|
+
} finally {
|
|
2100
|
+
setAttaching(false);
|
|
2101
|
+
}
|
|
2102
|
+
};
|
|
2103
|
+
const addDocument = async (documentId) => {
|
|
2104
|
+
if (!documents) return;
|
|
2105
|
+
const used = attachments.reduce((n, a) => n + a.size, 0);
|
|
2106
|
+
setError("");
|
|
2107
|
+
setAttaching(true);
|
|
2108
|
+
try {
|
|
2109
|
+
const added = await documents.toAttachment(documentId, maxBytes - used);
|
|
2110
|
+
setAttachments((previous) => [...previous, added]);
|
|
2111
|
+
setSaved(false);
|
|
2112
|
+
setDirty(true);
|
|
2113
|
+
} catch (err) {
|
|
2114
|
+
setError(err instanceof Error ? err.message : "Could not attach the document");
|
|
2115
|
+
} finally {
|
|
2116
|
+
setAttaching(false);
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
const close = () => {
|
|
2120
|
+
if (busy) return;
|
|
2121
|
+
if (!saved && (dirty || !draftId) && (text || subject || to || cc || bcc || attachments.length) && !window.confirm("Close this message without saving your latest changes?"))
|
|
2122
|
+
return;
|
|
2123
|
+
onClose();
|
|
2124
|
+
};
|
|
2125
|
+
useEffect(() => {
|
|
2126
|
+
if (closeRequestRef) closeRequestRef.current = close;
|
|
2127
|
+
});
|
|
2128
|
+
useEffect(() => {
|
|
2129
|
+
onChange?.(body());
|
|
2130
|
+
}, [to, cc, bcc, subject, text, html, attachments, draftId, secure]);
|
|
2131
|
+
const form = /* @__PURE__ */ jsxs(
|
|
2132
|
+
"form",
|
|
2133
|
+
{
|
|
2134
|
+
onKeyDown: (event) => {
|
|
2135
|
+
if (event.key !== "Enter") return;
|
|
2136
|
+
if (event.ctrlKey || event.metaKey) {
|
|
2137
|
+
event.preventDefault();
|
|
2138
|
+
if (ready && !busy) event.currentTarget.requestSubmit();
|
|
2139
|
+
} else if (event.target instanceof HTMLInputElement) {
|
|
2140
|
+
event.preventDefault();
|
|
2141
|
+
}
|
|
2142
|
+
},
|
|
2143
|
+
onSubmit: (e) => {
|
|
2144
|
+
e.preventDefault();
|
|
2145
|
+
setError("");
|
|
2146
|
+
sendMessage();
|
|
2147
|
+
},
|
|
2148
|
+
className: embedded ? "flex h-full min-h-0 w-full flex-col bg-background" : "flex max-h-[95dvh] w-full max-w-3xl flex-col rounded-xl border bg-background shadow-2xl",
|
|
2149
|
+
children: [
|
|
2150
|
+
!embedded && /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between border-b px-5 py-3", children: [
|
|
2151
|
+
/* @__PURE__ */ jsx("h2", { className: "font-semibold", children: "New message" }),
|
|
2152
|
+
/* @__PURE__ */ jsx(
|
|
2153
|
+
Button,
|
|
2154
|
+
{
|
|
2155
|
+
type: "button",
|
|
2156
|
+
variant: "ghost",
|
|
2157
|
+
"aria-label": "Close message",
|
|
2158
|
+
disabled: busy,
|
|
2159
|
+
onClick: close,
|
|
2160
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4" })
|
|
2161
|
+
}
|
|
2162
|
+
)
|
|
2163
|
+
] }),
|
|
2164
|
+
/* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-auto px-5 py-3", children: [
|
|
2165
|
+
sameSubjectDraft && /* @__PURE__ */ jsxs(
|
|
2166
|
+
"div",
|
|
2167
|
+
{
|
|
2168
|
+
role: "status",
|
|
2169
|
+
className: "mb-3 flex items-center justify-between gap-3 rounded-md bg-muted/40 px-3 py-2 text-xs text-muted-foreground",
|
|
2170
|
+
children: [
|
|
2171
|
+
"A draft with this subject is already in Drafts.",
|
|
2172
|
+
onOpenDraft && /* @__PURE__ */ jsx(
|
|
2173
|
+
Button,
|
|
2174
|
+
{
|
|
2175
|
+
type: "button",
|
|
2176
|
+
variant: "ghost",
|
|
2177
|
+
className: "h-7 shrink-0 text-xs",
|
|
2178
|
+
onClick: () => onOpenDraft(sameSubjectDraft.id),
|
|
2179
|
+
children: "Open draft"
|
|
2180
|
+
}
|
|
2181
|
+
)
|
|
2182
|
+
]
|
|
2183
|
+
}
|
|
2184
|
+
),
|
|
2185
|
+
/* @__PURE__ */ jsxs("p", { className: "mb-2 flex items-center gap-3 border-b border-border/60 pb-3 text-xs text-muted-foreground", children: [
|
|
2186
|
+
/* @__PURE__ */ jsx("span", { className: "w-14 shrink-0", children: "From" }),
|
|
2187
|
+
/* @__PURE__ */ jsx("span", { className: "rounded bg-muted/50 px-2 py-1 text-foreground", children: account?.email ?? "Connect a mailbox to send" })
|
|
2188
|
+
] }),
|
|
2189
|
+
[
|
|
2190
|
+
["To", to, setTo],
|
|
2191
|
+
["Cc", cc, setCc],
|
|
2192
|
+
["Bcc", bcc, setBcc]
|
|
2193
|
+
].filter(
|
|
2194
|
+
([label]) => label === "To" || label === "Cc" && showCc || label === "Bcc" && showBcc
|
|
2195
|
+
).map(([label, value, setter]) => /* @__PURE__ */ jsxs(
|
|
2196
|
+
"label",
|
|
2197
|
+
{
|
|
2198
|
+
className: "flex items-center gap-3 border-b border-border/60 py-2.5 text-sm focus-within:border-primary",
|
|
2199
|
+
children: [
|
|
2200
|
+
/* @__PURE__ */ jsx("span", { className: "w-14 shrink-0 text-xs text-muted-foreground", children: label }),
|
|
2201
|
+
/* @__PURE__ */ jsx(
|
|
2202
|
+
"input",
|
|
2203
|
+
{
|
|
2204
|
+
"aria-label": label,
|
|
2205
|
+
required: label === "To" && !cc && !bcc,
|
|
2206
|
+
disabled: locked,
|
|
2207
|
+
className: "min-w-0 flex-1 bg-transparent outline-none",
|
|
2208
|
+
value,
|
|
2209
|
+
placeholder: label === "To" ? "Add recipients" : "",
|
|
2210
|
+
onChange: (e) => {
|
|
2211
|
+
setter(e.target.value);
|
|
2212
|
+
setSaved(false);
|
|
2213
|
+
setDirty(true);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
),
|
|
2217
|
+
label === "To" && /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 gap-2", children: [
|
|
2218
|
+
!showCc && /* @__PURE__ */ jsx(
|
|
2219
|
+
"button",
|
|
2220
|
+
{
|
|
2221
|
+
type: "button",
|
|
2222
|
+
disabled: locked,
|
|
2223
|
+
className: "text-xs text-muted-foreground hover:text-primary",
|
|
2224
|
+
onClick: () => setShowCc(true),
|
|
2225
|
+
children: "Cc"
|
|
2226
|
+
}
|
|
2227
|
+
),
|
|
2228
|
+
!showBcc && /* @__PURE__ */ jsx(
|
|
2229
|
+
"button",
|
|
2230
|
+
{
|
|
2231
|
+
type: "button",
|
|
2232
|
+
disabled: locked,
|
|
2233
|
+
className: "text-xs text-muted-foreground hover:text-primary",
|
|
2234
|
+
onClick: () => setShowBcc(true),
|
|
2235
|
+
children: "Bcc"
|
|
2236
|
+
}
|
|
2237
|
+
)
|
|
2238
|
+
] })
|
|
2239
|
+
]
|
|
2240
|
+
},
|
|
2241
|
+
label
|
|
2242
|
+
)),
|
|
2243
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-3 border-b border-border/60 py-2.5 text-sm focus-within:border-primary", children: [
|
|
2244
|
+
/* @__PURE__ */ jsx("span", { className: "w-14 shrink-0 text-xs text-muted-foreground", children: "Subject" }),
|
|
2245
|
+
/* @__PURE__ */ jsx(
|
|
2246
|
+
"input",
|
|
2247
|
+
{
|
|
2248
|
+
"aria-label": "Subject",
|
|
2249
|
+
placeholder: "Add a subject",
|
|
2250
|
+
disabled: locked,
|
|
2251
|
+
className: "min-w-0 flex-1 bg-transparent outline-none",
|
|
2252
|
+
value: subject,
|
|
2253
|
+
onChange: (e) => {
|
|
2254
|
+
setSubject(e.target.value);
|
|
2255
|
+
setSaved(false);
|
|
2256
|
+
setDirty(true);
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
)
|
|
2260
|
+
] }),
|
|
2261
|
+
/* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("p", { className: "py-4 text-sm text-muted-foreground", children: "Loading editor\u2026" }), children: /* @__PURE__ */ jsx(
|
|
2262
|
+
MailBodyEditor,
|
|
2263
|
+
{
|
|
2264
|
+
text,
|
|
2265
|
+
html,
|
|
2266
|
+
disabled: locked,
|
|
2267
|
+
onChange: (nextText, nextHtml) => {
|
|
2268
|
+
setText(nextText);
|
|
2269
|
+
setHtml(nextHtml);
|
|
2270
|
+
setSaved(false);
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
) }),
|
|
2274
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: attachments.map((attachment, index) => /* @__PURE__ */ jsxs(
|
|
2275
|
+
"span",
|
|
2276
|
+
{
|
|
2277
|
+
className: "flex items-center gap-2 rounded-md border border-border/70 bg-muted/30 px-3 py-2 text-xs",
|
|
2278
|
+
children: [
|
|
2279
|
+
/* @__PURE__ */ jsx(Paperclip, { className: "size-3" }),
|
|
2280
|
+
attachment.name,
|
|
2281
|
+
/* @__PURE__ */ jsx(
|
|
2282
|
+
"button",
|
|
2283
|
+
{
|
|
2284
|
+
type: "button",
|
|
2285
|
+
disabled: locked,
|
|
2286
|
+
"aria-label": `Remove ${attachment.name}`,
|
|
2287
|
+
onClick: () => {
|
|
2288
|
+
setAttachments((a) => a.filter((_, i) => i !== index));
|
|
2289
|
+
setSaved(false);
|
|
2290
|
+
setDirty(true);
|
|
2291
|
+
},
|
|
2292
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-3" })
|
|
2293
|
+
}
|
|
2294
|
+
)
|
|
2295
|
+
]
|
|
2296
|
+
},
|
|
2297
|
+
`${attachment.name}-${index}`
|
|
2298
|
+
)) }),
|
|
2299
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-3 text-sm text-destructive", children: error })
|
|
2300
|
+
] }),
|
|
2301
|
+
/* @__PURE__ */ jsxs("footer", { className: "flex flex-wrap items-center gap-1 border-t border-border/60 bg-muted/20 px-4 py-2 [&_button]:h-8 [&_button]:text-xs", children: [
|
|
2302
|
+
/* @__PURE__ */ jsx(
|
|
2303
|
+
Button,
|
|
2304
|
+
{
|
|
2305
|
+
type: "button",
|
|
2306
|
+
variant: "outline",
|
|
2307
|
+
disabled: !ready || locked,
|
|
2308
|
+
onClick: () => {
|
|
2309
|
+
setError("");
|
|
2310
|
+
saveDraft();
|
|
2311
|
+
},
|
|
2312
|
+
children: save.isPending ? "Saving\u2026" : "Save draft"
|
|
2313
|
+
}
|
|
2314
|
+
),
|
|
2315
|
+
/* @__PURE__ */ jsxs(
|
|
2316
|
+
Button,
|
|
2317
|
+
{
|
|
2318
|
+
type: "button",
|
|
2319
|
+
variant: "ghost",
|
|
2320
|
+
disabled: locked,
|
|
2321
|
+
onClick: () => fileInput.current?.click(),
|
|
2322
|
+
children: [
|
|
2323
|
+
/* @__PURE__ */ jsx(Paperclip, { className: "mr-2 size-4" }),
|
|
2324
|
+
"Attach"
|
|
2325
|
+
]
|
|
2326
|
+
}
|
|
2327
|
+
),
|
|
2328
|
+
documents && /* @__PURE__ */ jsx(
|
|
2329
|
+
MailDocumentPicker,
|
|
2330
|
+
{
|
|
2331
|
+
documents: documents.documents,
|
|
2332
|
+
disabled: locked,
|
|
2333
|
+
onAttach: (doc) => void addDocument(doc.id)
|
|
2334
|
+
}
|
|
2335
|
+
),
|
|
2336
|
+
/* @__PURE__ */ jsx(
|
|
2337
|
+
"input",
|
|
2338
|
+
{
|
|
2339
|
+
ref: fileInput,
|
|
2340
|
+
type: "file",
|
|
2341
|
+
multiple: true,
|
|
2342
|
+
hidden: true,
|
|
2343
|
+
onChange: (e) => {
|
|
2344
|
+
void addFiles(e.target.files);
|
|
2345
|
+
e.target.value = "";
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
),
|
|
2349
|
+
saved && /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap text-xs text-muted-foreground", children: savedLocally ? "Saved on this device \xB7 syncs to Drafts when online" : "Saved to Drafts" }),
|
|
2350
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
|
|
2351
|
+
account?.provider === "native" && client.prepareSecureMail && /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
2352
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, showWithText: true, children: /* @__PURE__ */ jsxs("label", { className: "flex h-8 shrink-0 cursor-pointer items-center gap-1.5 rounded border border-border/70 px-2 text-xs text-primary", children: [
|
|
2353
|
+
/* @__PURE__ */ jsx(
|
|
2354
|
+
"input",
|
|
2355
|
+
{
|
|
2356
|
+
type: "checkbox",
|
|
2357
|
+
className: "size-3.5 accent-primary",
|
|
2358
|
+
checked: secure,
|
|
2359
|
+
disabled: locked,
|
|
2360
|
+
onChange: (e) => setSecure(e.target.checked)
|
|
2361
|
+
}
|
|
2362
|
+
),
|
|
2363
|
+
"Secure"
|
|
2364
|
+
] }) }),
|
|
2365
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", children: "The recipient will receive a secure link to access the message through the platform." })
|
|
2366
|
+
] }),
|
|
2367
|
+
/* @__PURE__ */ jsxs(
|
|
2368
|
+
Button,
|
|
2369
|
+
{
|
|
2370
|
+
type: "submit",
|
|
2371
|
+
className: "h-8 rounded bg-primary px-4 text-primary-foreground shadow-none hover:bg-primary/90",
|
|
2372
|
+
disabled: !ready || busy,
|
|
2373
|
+
children: [
|
|
2374
|
+
/* @__PURE__ */ jsx(Send, { className: "mr-2 size-4" }),
|
|
2375
|
+
send.isPending ? "Sending\u2026" : attemptedPayload.current ? "Check send result" : "Send"
|
|
2376
|
+
]
|
|
2377
|
+
}
|
|
2378
|
+
)
|
|
2379
|
+
] }),
|
|
2380
|
+
offline && !isOnline && /* @__PURE__ */ jsx(
|
|
2381
|
+
"p",
|
|
2382
|
+
{
|
|
2383
|
+
className: "w-full text-[11px] text-muted-foreground",
|
|
2384
|
+
"data-testid": "compose-offline-hint",
|
|
2385
|
+
children: "You're offline. Send puts this message in your Outbox; it goes out when you reconnect."
|
|
2386
|
+
}
|
|
2387
|
+
)
|
|
2388
|
+
] })
|
|
2389
|
+
]
|
|
2390
|
+
}
|
|
2391
|
+
);
|
|
2392
|
+
if (embedded)
|
|
2393
|
+
return /* @__PURE__ */ jsx("div", { role: "dialog", "aria-label": "Compose email", className: "h-full min-h-0", children: form });
|
|
2394
|
+
return /* @__PURE__ */ jsx(
|
|
2395
|
+
Dialog,
|
|
2396
|
+
{
|
|
2397
|
+
open: true,
|
|
2398
|
+
onOpenChange: (open) => {
|
|
2399
|
+
if (!open && !busy) close();
|
|
2400
|
+
},
|
|
2401
|
+
children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[95dvh] max-w-3xl overflow-auto p-0", children: [
|
|
2402
|
+
/* @__PURE__ */ jsx(DialogTitle, { className: "sr-only", children: "Compose email" }),
|
|
2403
|
+
/* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: "Write a message and attach files." }),
|
|
2404
|
+
form
|
|
2405
|
+
] })
|
|
2406
|
+
}
|
|
2407
|
+
);
|
|
2408
|
+
}
|
|
2409
|
+
var defaultMailNotify = (message, variant, options) => {
|
|
2410
|
+
notify[variant](message, options);
|
|
2411
|
+
};
|
|
2412
|
+
function resolveMailbox(accounts, mailbox) {
|
|
2413
|
+
if (!accounts) return void 0;
|
|
2414
|
+
if (mailbox === "native") return accounts.find((a) => a.provider === "native");
|
|
2415
|
+
return mailbox ? accounts.find((a) => a.id === mailbox) : void 0;
|
|
2416
|
+
}
|
|
2417
|
+
var DEFAULT_MAX_BYTES = 15 * 1024 * 1024;
|
|
2418
|
+
function ConnectedMailComposer({
|
|
2419
|
+
draftId,
|
|
2420
|
+
initial,
|
|
2421
|
+
account: accountProp,
|
|
2422
|
+
mailbox = "native",
|
|
2423
|
+
maxBytes: maxBytesProp,
|
|
2424
|
+
ready: readyProp,
|
|
2425
|
+
drafts: draftsProp,
|
|
2426
|
+
notify: notify3 = defaultMailNotify,
|
|
2427
|
+
offline,
|
|
2428
|
+
...rest
|
|
2429
|
+
}) {
|
|
2430
|
+
const { client } = useMailClient();
|
|
2431
|
+
const isOnline = useMailOnline();
|
|
2432
|
+
const policy = useMailPolicy();
|
|
2433
|
+
const accounts = useMailAccounts({ enabled: !accountProp && !!policy.data?.enabled });
|
|
2434
|
+
const account = accountProp ?? resolveMailbox(accounts.data, mailbox);
|
|
2435
|
+
const unlock = useMailUnlock(readyProp === void 0 ? account : void 0);
|
|
2436
|
+
const ready = readyProp ?? (!!account && (!!unlock.data || !isOnline));
|
|
2437
|
+
const folders = useMailFolders(account, { enabled: !draftsProp && ready });
|
|
2438
|
+
const draftsFolderId = folders.data?.find((f) => f.role === "drafts")?.id;
|
|
2439
|
+
const draftsList = useMailMessages(account, draftsFolderId, "", null, {
|
|
2440
|
+
enabled: !draftsProp && ready
|
|
2441
|
+
});
|
|
2442
|
+
const draft = useMailDraftCompose(
|
|
2443
|
+
account,
|
|
2444
|
+
draftId,
|
|
2445
|
+
async (from, id) => {
|
|
2446
|
+
const message = await client.getMessage(from, id);
|
|
2447
|
+
return draftMailCompose(
|
|
2448
|
+
message,
|
|
2449
|
+
await hydrateMailAttachments(client, from, message.id, message.attachments, {
|
|
2450
|
+
isOnline,
|
|
2451
|
+
offline
|
|
2452
|
+
})
|
|
2453
|
+
);
|
|
2454
|
+
},
|
|
2455
|
+
{ enabled: !initial && ready }
|
|
2456
|
+
);
|
|
2457
|
+
const start = useMemo(
|
|
2458
|
+
() => initial ?? (draftId ? draft.data : emptyMailCompose()),
|
|
2459
|
+
[initial, draftId, draft.data]
|
|
2460
|
+
);
|
|
2461
|
+
if (!start)
|
|
2462
|
+
return draft.error ? /* @__PURE__ */ jsx("p", { role: "alert", className: "p-4 text-sm text-destructive", children: draft.error.message }) : /* @__PURE__ */ jsx("p", { className: "p-4 text-sm text-muted-foreground", children: "Loading draft\u2026" });
|
|
2463
|
+
return /* @__PURE__ */ jsx(
|
|
2464
|
+
MailComposerForm,
|
|
2465
|
+
{
|
|
2466
|
+
account,
|
|
2467
|
+
initial: start,
|
|
2468
|
+
maxBytes: maxBytesProp ?? policy.data?.max_attachment_bytes ?? DEFAULT_MAX_BYTES,
|
|
2469
|
+
ready,
|
|
2470
|
+
drafts: draftsProp ?? draftsList.data?.messages ?? [],
|
|
2471
|
+
notify: notify3,
|
|
2472
|
+
offline,
|
|
2473
|
+
...rest
|
|
2474
|
+
},
|
|
2475
|
+
`${account?.id}-${draftId ?? ""}`
|
|
2476
|
+
);
|
|
2477
|
+
}
|
|
2478
|
+
function ConnectedMailSetup({
|
|
2479
|
+
policy,
|
|
2480
|
+
user,
|
|
2481
|
+
productName,
|
|
2482
|
+
onClaimed,
|
|
2483
|
+
onConnectExisting
|
|
2484
|
+
}) {
|
|
2485
|
+
const isOnline = useMailOnline();
|
|
2486
|
+
const [alias, setAlias] = useState(() => defaultMailAlias(user));
|
|
2487
|
+
const [error, setError] = useState("");
|
|
2488
|
+
const claim = useClaimNativeAlias(onClaimed);
|
|
2489
|
+
const hasExternal = policy.oauth_providers.length > 0 || policy.server_profiles.length > 0;
|
|
2490
|
+
const onClaimError = (err) => {
|
|
2491
|
+
if (!isAliasTakenError(err)) return setError(err.message);
|
|
2492
|
+
const suggestion = nextAliasSuggestion(alias);
|
|
2493
|
+
setAlias(suggestion);
|
|
2494
|
+
setError(aliasTakenMessage(alias, suggestion, policy.native_domain));
|
|
2495
|
+
};
|
|
2496
|
+
return /* @__PURE__ */ jsxs(
|
|
2497
|
+
"div",
|
|
2498
|
+
{
|
|
2499
|
+
"data-testid": "mail-setup",
|
|
2500
|
+
className: "mx-auto flex max-w-xl flex-col items-center gap-6 px-6 py-16 text-center",
|
|
2501
|
+
children: [
|
|
2502
|
+
/* @__PURE__ */ jsx(Mail, { className: "size-10 text-muted-foreground", "aria-hidden": true }),
|
|
2503
|
+
policy.native_enabled ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2504
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2505
|
+
/* @__PURE__ */ jsxs("h2", { className: "text-xl font-semibold text-foreground", children: [
|
|
2506
|
+
"Set up your ",
|
|
2507
|
+
productName ? `${productName} ` : "",
|
|
2508
|
+
"email"
|
|
2509
|
+
] }),
|
|
2510
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Confirm your address and you are ready to go: you can send and receive email as" })
|
|
2511
|
+
] }),
|
|
2512
|
+
/* @__PURE__ */ jsxs(
|
|
2513
|
+
"form",
|
|
2514
|
+
{
|
|
2515
|
+
className: "flex w-full flex-col items-center gap-4",
|
|
2516
|
+
onSubmit: (e) => {
|
|
2517
|
+
e.preventDefault();
|
|
2518
|
+
setError("");
|
|
2519
|
+
claim.mutate(alias, { onError: onClaimError });
|
|
2520
|
+
},
|
|
2521
|
+
children: [
|
|
2522
|
+
/* @__PURE__ */ jsxs("div", { className: "flex w-full items-center justify-center gap-2 text-base", children: [
|
|
2523
|
+
/* @__PURE__ */ jsx(
|
|
2524
|
+
Input,
|
|
2525
|
+
{
|
|
2526
|
+
"aria-label": "Email alias",
|
|
2527
|
+
required: true,
|
|
2528
|
+
pattern: "[a-z0-9][a-z0-9._\\-]{0,62}",
|
|
2529
|
+
autoFocus: true,
|
|
2530
|
+
className: "max-w-60 text-right",
|
|
2531
|
+
value: alias,
|
|
2532
|
+
onChange: (e) => setAlias(e.target.value.toLowerCase())
|
|
2533
|
+
}
|
|
2534
|
+
),
|
|
2535
|
+
/* @__PURE__ */ jsxs("span", { className: "text-foreground", children: [
|
|
2536
|
+
"@",
|
|
2537
|
+
policy.native_domain
|
|
2538
|
+
] })
|
|
2539
|
+
] }),
|
|
2540
|
+
/* @__PURE__ */ jsx(
|
|
2541
|
+
Button,
|
|
2542
|
+
{
|
|
2543
|
+
type: "submit",
|
|
2544
|
+
disabled: claim.isPending || !alias || !isOnline,
|
|
2545
|
+
title: isOnline ? void 0 : "Available when you are back online",
|
|
2546
|
+
children: claim.isPending ? "Setting up\u2026" : "Confirm"
|
|
2547
|
+
}
|
|
2548
|
+
)
|
|
2549
|
+
]
|
|
2550
|
+
}
|
|
2551
|
+
)
|
|
2552
|
+
] }) : /* @__PURE__ */ jsxs("div", { children: [
|
|
2553
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-foreground", children: "Connect your email" }),
|
|
2554
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-2 text-sm text-muted-foreground", children: [
|
|
2555
|
+
"Connect an existing account to read and send email",
|
|
2556
|
+
" ",
|
|
2557
|
+
productName ? `from ${productName}` : "here",
|
|
2558
|
+
"."
|
|
2559
|
+
] })
|
|
2560
|
+
] }),
|
|
2561
|
+
hasExternal && /* @__PURE__ */ jsx(
|
|
2562
|
+
Button,
|
|
2563
|
+
{
|
|
2564
|
+
variant: policy.native_enabled ? "ghost" : "default",
|
|
2565
|
+
onClick: onConnectExisting,
|
|
2566
|
+
className: policy.native_enabled ? "text-muted-foreground" : void 0,
|
|
2567
|
+
children: policy.native_enabled ? "Connect an existing email account instead" : "Connect an email account"
|
|
2568
|
+
}
|
|
2569
|
+
),
|
|
2570
|
+
!policy.native_enabled && !hasExternal && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Mail is not set up for this deployment yet. Ask your administrator." }),
|
|
2571
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm text-destructive", children: error })
|
|
2572
|
+
]
|
|
2573
|
+
}
|
|
2574
|
+
);
|
|
2575
|
+
}
|
|
2576
|
+
var NO_DRAFTS = [];
|
|
2577
|
+
var NO_OUTBOX = [];
|
|
2578
|
+
var LOCAL_DRAFT_PREFIX = "local-draft:";
|
|
2579
|
+
var OFFLINE_FOLDERS = [
|
|
2580
|
+
{ id: "device:drafts", name: "Drafts", role: "drafts", unread: 0 }
|
|
2581
|
+
];
|
|
2582
|
+
function localDraftRow(draft) {
|
|
2583
|
+
return {
|
|
2584
|
+
id: LOCAL_DRAFT_PREFIX + draft.id,
|
|
2585
|
+
subject: draft.compose.subject,
|
|
2586
|
+
sender: [],
|
|
2587
|
+
to: draft.compose.to,
|
|
2588
|
+
cc: draft.compose.cc,
|
|
2589
|
+
bcc: draft.compose.bcc,
|
|
2590
|
+
received_at: new Date(draft.updatedAt).toISOString(),
|
|
2591
|
+
preview: draft.error ? `Saved on this device only: ${draft.error}` : "Saved on this device \xB7 syncs to Drafts when online",
|
|
2592
|
+
unread: false,
|
|
2593
|
+
text: "",
|
|
2594
|
+
references: [],
|
|
2595
|
+
attachments: draft.compose.attachments.map((a) => ({ ...a, content_base64: null }))
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
function applyQuickFilters(messages, filters) {
|
|
2599
|
+
const included = [...filters].filter(([, s]) => s === "include").map(([k]) => k);
|
|
2600
|
+
const excluded = [...filters].filter(([, s]) => s === "exclude").map(([k]) => k);
|
|
2601
|
+
if (!included.length && !excluded.length) return messages;
|
|
2602
|
+
return messages.filter((m) => {
|
|
2603
|
+
const has = {
|
|
2604
|
+
unread: m.unread,
|
|
2605
|
+
attachments: m.attachments.length > 0,
|
|
2606
|
+
secure: !!m.secure_delivery
|
|
2607
|
+
};
|
|
2608
|
+
if (excluded.some((k) => has[k])) return false;
|
|
2609
|
+
return !included.length || included.some((k) => has[k]);
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
function SidePanel({ isOpen, title, onClose, children }) {
|
|
2613
|
+
if (!isOpen) return null;
|
|
2614
|
+
return /* @__PURE__ */ jsxs(
|
|
2615
|
+
"aside",
|
|
2616
|
+
{
|
|
2617
|
+
"aria-label": title,
|
|
2618
|
+
className: "flex min-h-0 w-full max-w-2xl shrink-0 flex-col border-l border-border bg-background",
|
|
2619
|
+
children: [
|
|
2620
|
+
/* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-2 border-b px-3 py-2", children: [
|
|
2621
|
+
/* @__PURE__ */ jsx("h2", { className: "truncate text-sm font-semibold", children: title }),
|
|
2622
|
+
/* @__PURE__ */ jsx(
|
|
2623
|
+
Button,
|
|
2624
|
+
{
|
|
2625
|
+
variant: "ghost",
|
|
2626
|
+
size: "sm",
|
|
2627
|
+
"aria-label": "Close",
|
|
2628
|
+
"data-testid": "close-mail-viewer",
|
|
2629
|
+
onClick: onClose,
|
|
2630
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4" })
|
|
2631
|
+
}
|
|
2632
|
+
)
|
|
2633
|
+
] }),
|
|
2634
|
+
/* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1", children })
|
|
2635
|
+
]
|
|
2636
|
+
}
|
|
2637
|
+
);
|
|
2638
|
+
}
|
|
2639
|
+
function ConnectedMailbox({
|
|
2640
|
+
mailbox,
|
|
2641
|
+
draftId,
|
|
2642
|
+
onDraftOpened,
|
|
2643
|
+
folder: requestedFolder,
|
|
2644
|
+
onFolderOpened,
|
|
2645
|
+
composeAttachments,
|
|
2646
|
+
onComposeOpened,
|
|
2647
|
+
onOpenDocument,
|
|
2648
|
+
user,
|
|
2649
|
+
productName,
|
|
2650
|
+
oauthReturnPath,
|
|
2651
|
+
personLabel,
|
|
2652
|
+
offline,
|
|
2653
|
+
documents,
|
|
2654
|
+
saveToWorkspace,
|
|
2655
|
+
renderInvite,
|
|
2656
|
+
renderPanel = SidePanel,
|
|
2657
|
+
onViewerOpenChange,
|
|
2658
|
+
gridState,
|
|
2659
|
+
onGridStateChange,
|
|
2660
|
+
initialMessageId = null,
|
|
2661
|
+
notify: notify3 = defaultMailNotify
|
|
2662
|
+
}) {
|
|
2663
|
+
const cache = useMailCache();
|
|
2664
|
+
const { client } = useMailClient();
|
|
2665
|
+
const [savingToWorkspace, setSavingToWorkspace] = useState(false);
|
|
2666
|
+
const [savePending, setSavePending] = useState(false);
|
|
2667
|
+
const [saveWorkspaceId, setSaveWorkspaceId] = useState("");
|
|
2668
|
+
const { data: policy, error: policyError } = useMailPolicy();
|
|
2669
|
+
const isOnline = useMailOnline();
|
|
2670
|
+
const accounts = useMailAccounts({ enabled: !!policy?.enabled });
|
|
2671
|
+
const [accountId, setAccountId] = useState("");
|
|
2672
|
+
const account = accounts.data?.find((a) => a.id === accountId) ?? resolveMailbox(accounts.data, mailbox) ?? accounts.data?.[0];
|
|
2673
|
+
const unlock = useMailUnlock(account);
|
|
2674
|
+
const [folder, setFolder] = useState("inbox");
|
|
2675
|
+
const [folderMenuOpen, setFolderMenuOpen] = useState(false);
|
|
2676
|
+
const [filterMenuOpen, setFilterMenuOpen] = useState(false);
|
|
2677
|
+
const [query, setQuery] = useState("");
|
|
2678
|
+
const [mailFilters, setMailFilters] = useState(/* @__PURE__ */ new Map());
|
|
2679
|
+
const [cursor, setCursor] = useState(null);
|
|
2680
|
+
const [selected, setSelected] = useState(initialMessageId);
|
|
2681
|
+
const [compose, setCompose] = useState(null);
|
|
2682
|
+
const [composeLocalDraftId, setComposeLocalDraftId] = useState();
|
|
2683
|
+
const [composeSession, setComposeSession] = useState(0);
|
|
2684
|
+
const openCompose = (value, localDraftId) => {
|
|
2685
|
+
setComposeSession((n) => n + 1);
|
|
2686
|
+
setComposeLocalDraftId(localDraftId);
|
|
2687
|
+
setCompose(value);
|
|
2688
|
+
};
|
|
2689
|
+
const composeClose = useRef(null);
|
|
2690
|
+
useEffect(() => {
|
|
2691
|
+
onViewerOpenChange?.(!!selected || !!compose);
|
|
2692
|
+
}, [selected, compose, onViewerOpenChange]);
|
|
2693
|
+
useEffect(() => () => onViewerOpenChange?.(false), [onViewerOpenChange]);
|
|
2694
|
+
const [settings, setSettings] = useState(false);
|
|
2695
|
+
const [justCreated, setJustCreated] = useState(false);
|
|
2696
|
+
const [actionError, setActionError] = useState("");
|
|
2697
|
+
const ready = !!account && (!!unlock.data || !isOnline);
|
|
2698
|
+
useEffect(() => {
|
|
2699
|
+
if (unlock.data) void cache.refreshFolders();
|
|
2700
|
+
}, [unlock.data, cache]);
|
|
2701
|
+
const folders = useMailFolders(account, { enabled: ready });
|
|
2702
|
+
const outbox = offline?.useOutbox() ?? NO_OUTBOX;
|
|
2703
|
+
const outboxRole = offline?.outboxRole;
|
|
2704
|
+
const waitingInOutbox = outbox.filter(
|
|
2705
|
+
(item) => item.account.id === account?.id && item.status !== "sent"
|
|
2706
|
+
).length;
|
|
2707
|
+
const folderList = useMemo(() => {
|
|
2708
|
+
const base = folders.data ?? (offline && !isOnline && folders.error ? OFFLINE_FOLDERS : void 0);
|
|
2709
|
+
if (!base || !outboxRole) return base;
|
|
2710
|
+
const withCount = (f) => f.role === outboxRole ? { ...f, unread: f.unread + waitingInOutbox } : f;
|
|
2711
|
+
return base.some((f) => f.role === outboxRole) ? base.map(withCount) : [...base, withCount({ id: "device:outbox", name: "Outbox", role: outboxRole, unread: 0 })];
|
|
2712
|
+
}, [folders.data, folders.error, isOnline, waitingInOutbox, offline, outboxRole]);
|
|
2713
|
+
const folderId = folderList?.find((f) => f.role === folder || f.id === folder)?.id ?? folderList?.[0]?.id;
|
|
2714
|
+
const folderRole = folderList?.find((f) => f.id === folderId)?.role ?? folder;
|
|
2715
|
+
const deviceFolder = !!folderId?.startsWith("device:");
|
|
2716
|
+
const list = useMailMessages(account, folderId, query, cursor, {
|
|
2717
|
+
enabled: ready && !deviceFolder,
|
|
2718
|
+
refetchInterval: 6e4
|
|
2719
|
+
});
|
|
2720
|
+
const localDrafts = offline?.useLocalDrafts(account?.id) ?? NO_DRAFTS;
|
|
2721
|
+
const draftsFolderId = folders.data?.find((f) => f.role === "drafts")?.id;
|
|
2722
|
+
const drafts = useMailMessages(account, draftsFolderId, "", null, {
|
|
2723
|
+
enabled: ready && !!compose
|
|
2724
|
+
});
|
|
2725
|
+
const loadedMessages = useMemo(() => {
|
|
2726
|
+
if (folderRole !== "drafts") return list.data?.messages;
|
|
2727
|
+
const words = query.toLowerCase();
|
|
2728
|
+
const mine = localDrafts.map(localDraftRow).filter(
|
|
2729
|
+
(m) => !words || `${m.subject} ${m.to.map((r) => r.email).join(" ")}`.toLowerCase().includes(words)
|
|
2730
|
+
);
|
|
2731
|
+
return [...mine, ...list.data?.messages ?? []];
|
|
2732
|
+
}, [folderRole, list.data, localDrafts, query]);
|
|
2733
|
+
const filteredMessages = useMemo(
|
|
2734
|
+
() => applyQuickFilters(loadedMessages ?? [], mailFilters),
|
|
2735
|
+
[loadedMessages, mailFilters]
|
|
2736
|
+
);
|
|
2737
|
+
const mailFilterOptions = useMemo(
|
|
2738
|
+
() => [
|
|
2739
|
+
{
|
|
2740
|
+
id: "unread",
|
|
2741
|
+
label: "Unread",
|
|
2742
|
+
count: (loadedMessages ?? []).filter((m) => m.unread).length
|
|
2743
|
+
},
|
|
2744
|
+
{
|
|
2745
|
+
id: "attachments",
|
|
2746
|
+
label: "Has attachments",
|
|
2747
|
+
count: (loadedMessages ?? []).filter((m) => m.attachments.length > 0).length
|
|
2748
|
+
},
|
|
2749
|
+
{
|
|
2750
|
+
id: "secure",
|
|
2751
|
+
label: "Secure delivery",
|
|
2752
|
+
count: (loadedMessages ?? []).filter((m) => m.secure_delivery).length
|
|
2753
|
+
}
|
|
2754
|
+
],
|
|
2755
|
+
[loadedMessages]
|
|
2756
|
+
);
|
|
2757
|
+
const hasActiveFilters = !!query || [...mailFilters.values()].some(Boolean);
|
|
2758
|
+
const detail = useMailMessage(account, selected, { enabled: ready && !compose });
|
|
2759
|
+
const message = detail.data;
|
|
2760
|
+
useEffect(() => {
|
|
2761
|
+
if (!composeAttachments?.length) return;
|
|
2762
|
+
openCompose({ ...emptyMailCompose(), attachments: composeAttachments });
|
|
2763
|
+
onComposeOpened?.();
|
|
2764
|
+
}, [composeAttachments]);
|
|
2765
|
+
useEffect(() => {
|
|
2766
|
+
if (!requestedFolder) return;
|
|
2767
|
+
setFolder(requestedFolder);
|
|
2768
|
+
setCursor(null);
|
|
2769
|
+
setSelected(null);
|
|
2770
|
+
onFolderOpened?.();
|
|
2771
|
+
}, [requestedFolder]);
|
|
2772
|
+
const [pendingDraft, setPendingDraft] = useState(null);
|
|
2773
|
+
useEffect(() => {
|
|
2774
|
+
if (!draftId) return;
|
|
2775
|
+
composeClose.current?.();
|
|
2776
|
+
setFolder("drafts");
|
|
2777
|
+
setCursor(null);
|
|
2778
|
+
setSelected(null);
|
|
2779
|
+
setPendingDraft(draftId);
|
|
2780
|
+
onDraftOpened?.();
|
|
2781
|
+
}, [draftId]);
|
|
2782
|
+
useEffect(() => {
|
|
2783
|
+
if (!pendingDraft || !accounts.data) return;
|
|
2784
|
+
const native = accounts.data.find((a) => a.provider === "native");
|
|
2785
|
+
if (native) setAccountId(native.id);
|
|
2786
|
+
setSelected(pendingDraft);
|
|
2787
|
+
setPendingDraft(null);
|
|
2788
|
+
}, [pendingDraft, accounts.data]);
|
|
2789
|
+
useEffect(() => {
|
|
2790
|
+
const chosen = resolveMailbox(accounts.data, mailbox);
|
|
2791
|
+
if (!chosen) return;
|
|
2792
|
+
setAccountId(chosen.id);
|
|
2793
|
+
}, [mailbox, !!accounts.data]);
|
|
2794
|
+
const patch = (messageId, body) => mailJson(
|
|
2795
|
+
client.fetch,
|
|
2796
|
+
`${accountPath(account.id)}/messages/${encodeURIComponent(messageId)}`,
|
|
2797
|
+
{
|
|
2798
|
+
method: "PATCH",
|
|
2799
|
+
body: JSON.stringify(body)
|
|
2800
|
+
}
|
|
2801
|
+
);
|
|
2802
|
+
const update = async (body) => {
|
|
2803
|
+
if (!selected || !account || !isOnline) return;
|
|
2804
|
+
try {
|
|
2805
|
+
await patch(selected, body);
|
|
2806
|
+
await cache.refreshAll();
|
|
2807
|
+
if (body.folder_id || body.action) setSelected(null);
|
|
2808
|
+
} catch (e) {
|
|
2809
|
+
setActionError(e instanceof Error ? e.message : "Could not update message");
|
|
2810
|
+
}
|
|
2811
|
+
};
|
|
2812
|
+
const download = async (path, name) => {
|
|
2813
|
+
try {
|
|
2814
|
+
const response = await client.fetch(path);
|
|
2815
|
+
saveFile(await response.blob(), name);
|
|
2816
|
+
} catch (e) {
|
|
2817
|
+
setActionError(e instanceof Error ? e.message : "Download failed");
|
|
2818
|
+
}
|
|
2819
|
+
};
|
|
2820
|
+
const downloadAttachment = async (a) => {
|
|
2821
|
+
if (!message || !account) return;
|
|
2822
|
+
if (isOnline) {
|
|
2823
|
+
await download(attachmentPath(account.id, message.id, a.id), a.name);
|
|
2824
|
+
return;
|
|
2825
|
+
}
|
|
2826
|
+
const file = await offline?.loadAttachment(account.id, message.id, a);
|
|
2827
|
+
if (!file) {
|
|
2828
|
+
setActionError(`${a.name} isn't saved on this device. Open it when you're back online.`);
|
|
2829
|
+
return;
|
|
2830
|
+
}
|
|
2831
|
+
saveFile(file, a.name);
|
|
2832
|
+
};
|
|
2833
|
+
const saveEmail = async () => {
|
|
2834
|
+
if (!message || !account || !saveWorkspaceId || !saveToWorkspace) return;
|
|
2835
|
+
setSavePending(true);
|
|
2836
|
+
try {
|
|
2837
|
+
const response = await client.fetch(
|
|
2838
|
+
`${accountPath(account.id)}/messages/${message.id}/original`
|
|
2839
|
+
);
|
|
2840
|
+
const fileName = `${message.subject.replace(/[^a-zA-Z0-9 ._-]/g, "").slice(0, 80) || "email"}.eml`;
|
|
2841
|
+
const workspaceId = saveWorkspaceId;
|
|
2842
|
+
const documentId = await saveToWorkspace.save(
|
|
2843
|
+
workspaceId,
|
|
2844
|
+
new File([await response.blob()], fileName, { type: "message/rfc822" })
|
|
2845
|
+
);
|
|
2846
|
+
setSavingToWorkspace(false);
|
|
2847
|
+
notify3(
|
|
2848
|
+
"Email saved to workspace",
|
|
2849
|
+
"success",
|
|
2850
|
+
documentId && onOpenDocument ? { action: { label: "Open", onClick: () => onOpenDocument(documentId, workspaceId) } } : void 0
|
|
2851
|
+
);
|
|
2852
|
+
} catch (e) {
|
|
2853
|
+
setActionError(e instanceof Error ? e.message : "Could not save email to workspace");
|
|
2854
|
+
} finally {
|
|
2855
|
+
setSavePending(false);
|
|
2856
|
+
}
|
|
2857
|
+
};
|
|
2858
|
+
const hydrate = (attachments) => message && account ? hydrateMailAttachments(client, account, message.id, attachments, { isOnline, offline }) : Promise.resolve([]);
|
|
2859
|
+
const editDraft = async () => {
|
|
2860
|
+
if (!message) return;
|
|
2861
|
+
try {
|
|
2862
|
+
openCompose(draftMailCompose(message, await hydrate(message.attachments)));
|
|
2863
|
+
} catch (e) {
|
|
2864
|
+
setActionError(e instanceof Error ? e.message : "Could not load draft attachments");
|
|
2865
|
+
}
|
|
2866
|
+
};
|
|
2867
|
+
const reply = async (forward = false, all = false) => {
|
|
2868
|
+
if (!message || !account) return;
|
|
2869
|
+
try {
|
|
2870
|
+
const attachments = forward ? await hydrate(message.attachments) : [];
|
|
2871
|
+
openCompose(replyMailCompose(message, { account, folderRole, forward, all, attachments }));
|
|
2872
|
+
} catch (e) {
|
|
2873
|
+
setActionError(e instanceof Error ? e.message : "Could not prepare forwarded attachments");
|
|
2874
|
+
}
|
|
2875
|
+
};
|
|
2876
|
+
const failures = [policyError, accounts.error, unlock.error, folders.error, list.error];
|
|
2877
|
+
const offlineNotice = failures.find((e) => e instanceof MailOfflineUnavailableError)?.message;
|
|
2878
|
+
const error = actionError || failures.find((e) => e && !(e instanceof MailOfflineUnavailableError))?.message || (detail.error instanceof MailOfflineUnavailableError ? void 0 : detail.error?.message);
|
|
2879
|
+
return /* @__PURE__ */ jsxs("section", { className: "flex h-full min-h-0 flex-col bg-background", "aria-label": "Mail", children: [
|
|
2880
|
+
/* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-10 bg-background", children: [
|
|
2881
|
+
/* @__PURE__ */ jsx(
|
|
2882
|
+
TabHeader,
|
|
2883
|
+
{
|
|
2884
|
+
testId: "mail-tab-header",
|
|
2885
|
+
tabs: [
|
|
2886
|
+
{
|
|
2887
|
+
id: "mail",
|
|
2888
|
+
// Icon + count only, like the Files, Facts and Threads headers.
|
|
2889
|
+
label: "",
|
|
2890
|
+
count: filteredMessages.length,
|
|
2891
|
+
countIcon: /* @__PURE__ */ jsx(Mail, { className: "size-3.5" }),
|
|
2892
|
+
countPlacement: "leading",
|
|
2893
|
+
presentation: "count",
|
|
2894
|
+
hasActiveFilters,
|
|
2895
|
+
testId: "mail-tab"
|
|
2896
|
+
}
|
|
2897
|
+
],
|
|
2898
|
+
activeTabId: "mail",
|
|
2899
|
+
onTabChange: () => {
|
|
2900
|
+
},
|
|
2901
|
+
compactBreakpoint: 360,
|
|
2902
|
+
firstTabContent: account ? /* @__PURE__ */ jsxs(FilesFilterBar, { children: [
|
|
2903
|
+
/* @__PURE__ */ jsx(
|
|
2904
|
+
FilterInput,
|
|
2905
|
+
{
|
|
2906
|
+
value: query,
|
|
2907
|
+
onChange: (q) => {
|
|
2908
|
+
setQuery(q);
|
|
2909
|
+
setCursor(null);
|
|
2910
|
+
},
|
|
2911
|
+
hideFieldPicker: true,
|
|
2912
|
+
fields: ["text"],
|
|
2913
|
+
onFieldsChange: () => {
|
|
2914
|
+
},
|
|
2915
|
+
ariaLabel: "Filter mail",
|
|
2916
|
+
labels: { placeholder: "Filter..." },
|
|
2917
|
+
testIds: { root: "mail-search", input: "mail-filter-input" },
|
|
2918
|
+
className: "min-w-28 flex-1 sm:max-w-72",
|
|
2919
|
+
filter: /* @__PURE__ */ jsx(
|
|
2920
|
+
TriStateFilterMenu,
|
|
2921
|
+
{
|
|
2922
|
+
testId: "mail-bar-filter-button",
|
|
2923
|
+
open: filterMenuOpen,
|
|
2924
|
+
onOpenChange: setFilterMenuOpen,
|
|
2925
|
+
filterStates: mailFilters,
|
|
2926
|
+
onFiltersChange: setMailFilters,
|
|
2927
|
+
options: mailFilterOptions,
|
|
2928
|
+
groups: [{ label: "Messages", options: ["unread", "attachments", "secure"] }],
|
|
2929
|
+
renderChevron: true,
|
|
2930
|
+
disableHoverOpen: true,
|
|
2931
|
+
triggerClassName: "inline-flex items-center gap-0.5 h-6 rounded px-1 text-muted-foreground hover:bg-foreground/10 cursor-pointer transition-colors pointer-coarse:h-11! pointer-coarse:min-w-11 pointer-coarse:justify-center"
|
|
2932
|
+
}
|
|
2933
|
+
)
|
|
2934
|
+
}
|
|
2935
|
+
),
|
|
2936
|
+
folderList && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: /* @__PURE__ */ jsx(
|
|
2937
|
+
FolderFilterButton,
|
|
2938
|
+
{
|
|
2939
|
+
label: folderList.find((f) => f.id === folderId)?.name ?? "Folders",
|
|
2940
|
+
isActive: false,
|
|
2941
|
+
open: folderMenuOpen,
|
|
2942
|
+
onOpenChange: setFolderMenuOpen,
|
|
2943
|
+
testId: "mail-folder-button",
|
|
2944
|
+
children: folderList.map((item) => /* @__PURE__ */ jsx(
|
|
2945
|
+
FolderPickerRow,
|
|
2946
|
+
{
|
|
2947
|
+
label: item.name,
|
|
2948
|
+
count: item.unread > 0 ? item.unread : void 0,
|
|
2949
|
+
depth: 0,
|
|
2950
|
+
isActive: item.id === folderId,
|
|
2951
|
+
onClick: () => {
|
|
2952
|
+
setFolder(item.id);
|
|
2953
|
+
setCursor(null);
|
|
2954
|
+
setSelected(null);
|
|
2955
|
+
setFolderMenuOpen(false);
|
|
2956
|
+
}
|
|
2957
|
+
},
|
|
2958
|
+
item.id
|
|
2959
|
+
))
|
|
2960
|
+
}
|
|
2961
|
+
) })
|
|
2962
|
+
] }) : void 0,
|
|
2963
|
+
rightContent: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
|
|
2964
|
+
account && /* @__PURE__ */ jsx(
|
|
2965
|
+
Button,
|
|
2966
|
+
{
|
|
2967
|
+
size: "sm",
|
|
2968
|
+
variant: "ghost",
|
|
2969
|
+
"aria-label": "Mail settings",
|
|
2970
|
+
"data-testid": "mail-settings-button",
|
|
2971
|
+
onClick: () => setSettings(true),
|
|
2972
|
+
children: /* @__PURE__ */ jsx(Settings, { className: "size-4" })
|
|
2973
|
+
}
|
|
2974
|
+
),
|
|
2975
|
+
/* @__PURE__ */ jsxs(
|
|
2976
|
+
Button,
|
|
2977
|
+
{
|
|
2978
|
+
size: "sm",
|
|
2979
|
+
disabled: !ready || !!compose,
|
|
2980
|
+
onClick: () => openCompose(emptyMailCompose()),
|
|
2981
|
+
children: [
|
|
2982
|
+
/* @__PURE__ */ jsx(Plus, { className: "mr-1.5 size-4" }),
|
|
2983
|
+
"Email"
|
|
2984
|
+
]
|
|
2985
|
+
}
|
|
2986
|
+
)
|
|
2987
|
+
] })
|
|
2988
|
+
}
|
|
2989
|
+
),
|
|
2990
|
+
(accounts.data?.length ?? 0) > 1 && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 px-3 py-1", children: /* @__PURE__ */ jsx(
|
|
2991
|
+
"select",
|
|
2992
|
+
{
|
|
2993
|
+
"aria-label": "Mail account",
|
|
2994
|
+
disabled: !!compose,
|
|
2995
|
+
className: "min-w-0 max-w-64 rounded border-0 bg-transparent px-2 py-1.5 text-xs text-muted-foreground",
|
|
2996
|
+
value: account?.id ?? "",
|
|
2997
|
+
onChange: (e) => {
|
|
2998
|
+
setAccountId(e.target.value);
|
|
2999
|
+
setFolder("inbox");
|
|
3000
|
+
setCursor(null);
|
|
3001
|
+
setSelected(null);
|
|
3002
|
+
},
|
|
3003
|
+
children: accounts.data?.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.email }, a.id))
|
|
3004
|
+
}
|
|
3005
|
+
) })
|
|
3006
|
+
] }),
|
|
3007
|
+
account && offline?.renderStatus?.(),
|
|
3008
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "border-b p-3 text-sm text-destructive", children: error }),
|
|
3009
|
+
!error && offlineNotice && /* @__PURE__ */ jsx("p", { role: "status", className: "border-b p-3 text-sm text-muted-foreground", children: offlineNotice }),
|
|
3010
|
+
policy && !policy.enabled && /* @__PURE__ */ jsx("p", { className: "p-6 text-muted-foreground", children: "Mail is not enabled on this deployment." }),
|
|
3011
|
+
policy?.enabled && accounts.isSuccess && !account && /* @__PURE__ */ jsx(
|
|
3012
|
+
ConnectedMailSetup,
|
|
3013
|
+
{
|
|
3014
|
+
policy,
|
|
3015
|
+
user,
|
|
3016
|
+
productName,
|
|
3017
|
+
onClaimed: () => setJustCreated(true),
|
|
3018
|
+
onConnectExisting: () => setSettings(true)
|
|
3019
|
+
}
|
|
3020
|
+
),
|
|
3021
|
+
account && justCreated && /* @__PURE__ */ jsxs(
|
|
3022
|
+
"div",
|
|
3023
|
+
{
|
|
3024
|
+
role: "status",
|
|
3025
|
+
"data-testid": "mail-ready-banner",
|
|
3026
|
+
className: "flex items-start justify-between gap-3 border-b bg-accent/40 px-4 py-3 text-sm",
|
|
3027
|
+
children: [
|
|
3028
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
3029
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold", children: [
|
|
3030
|
+
"Your ",
|
|
3031
|
+
productName ? `${productName} ` : "",
|
|
3032
|
+
"email is ready to go."
|
|
3033
|
+
] }),
|
|
3034
|
+
" ",
|
|
3035
|
+
"You can send and receive email as ",
|
|
3036
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: account.email }),
|
|
3037
|
+
"."
|
|
3038
|
+
] }),
|
|
3039
|
+
/* @__PURE__ */ jsx(
|
|
3040
|
+
"button",
|
|
3041
|
+
{
|
|
3042
|
+
type: "button",
|
|
3043
|
+
"aria-label": "Dismiss",
|
|
3044
|
+
className: "text-muted-foreground hover:text-foreground",
|
|
3045
|
+
onClick: () => setJustCreated(false),
|
|
3046
|
+
children: "\xD7"
|
|
3047
|
+
}
|
|
3048
|
+
)
|
|
3049
|
+
]
|
|
3050
|
+
}
|
|
3051
|
+
),
|
|
3052
|
+
account && /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1", children: [
|
|
3053
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 w-full flex-col", children: [
|
|
3054
|
+
outboxRole && folderRole === outboxRole && offline?.renderOutbox({
|
|
3055
|
+
accountId: account.id,
|
|
3056
|
+
onEdit: (value) => openCompose({ ...value })
|
|
3057
|
+
}),
|
|
3058
|
+
/* @__PURE__ */ jsx("div", { className: deviceFolder ? "hidden" : "min-h-0 flex-1", children: /* @__PURE__ */ jsx(
|
|
3059
|
+
MailGrid,
|
|
3060
|
+
{
|
|
3061
|
+
messages: filteredMessages,
|
|
3062
|
+
loading: list.isPending && list.fetchStatus !== "idle",
|
|
3063
|
+
selected,
|
|
3064
|
+
outgoing: folderRole === "sent" || folderRole === "drafts" || folderRole === outboxRole,
|
|
3065
|
+
personLabel,
|
|
3066
|
+
gridState,
|
|
3067
|
+
onGridStateChange,
|
|
3068
|
+
onSelect: (id) => {
|
|
3069
|
+
if (id.startsWith(LOCAL_DRAFT_PREFIX)) {
|
|
3070
|
+
const draft = localDrafts.find((d) => LOCAL_DRAFT_PREFIX + d.id === id);
|
|
3071
|
+
if (draft) openCompose({ ...draft.compose }, draft.id);
|
|
3072
|
+
return;
|
|
3073
|
+
}
|
|
3074
|
+
setSelected(id);
|
|
3075
|
+
if (isOnline && list.data?.messages.find((m) => m.id === id)?.unread) {
|
|
3076
|
+
void patch(id, { unread: false }).then(() => cache.settleMessage(account.id, id)).catch((error2) => setActionError(error2.message));
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
) }),
|
|
3081
|
+
(cursor || list.data?.next_cursor) && /* @__PURE__ */ jsxs("footer", { className: "flex gap-2 p-2", children: [
|
|
3082
|
+
cursor && /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => setCursor(null), children: "First page" }),
|
|
3083
|
+
list.data?.next_cursor && /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => setCursor(list.data.next_cursor), children: "Next page" })
|
|
3084
|
+
] })
|
|
3085
|
+
] }),
|
|
3086
|
+
renderPanel({
|
|
3087
|
+
isOpen: !!selected || !!compose,
|
|
3088
|
+
title: compose ? "New message" : message?.subject || "Mail",
|
|
3089
|
+
onClose: () => {
|
|
3090
|
+
if (compose) composeClose.current?.();
|
|
3091
|
+
else setSelected(null);
|
|
3092
|
+
},
|
|
3093
|
+
children: compose ? /* @__PURE__ */ jsx(
|
|
3094
|
+
ConnectedMailComposer,
|
|
3095
|
+
{
|
|
3096
|
+
account,
|
|
3097
|
+
initial: compose,
|
|
3098
|
+
maxBytes: policy?.max_attachment_bytes ?? 15 * 1024 * 1024,
|
|
3099
|
+
ready,
|
|
3100
|
+
drafts: drafts.data?.messages ?? [],
|
|
3101
|
+
embedded: true,
|
|
3102
|
+
localDraftId: composeLocalDraftId,
|
|
3103
|
+
offline,
|
|
3104
|
+
documents,
|
|
3105
|
+
notify: notify3,
|
|
3106
|
+
onChange: setCompose,
|
|
3107
|
+
closeRequestRef: composeClose,
|
|
3108
|
+
onClose: () => {
|
|
3109
|
+
setCompose(null);
|
|
3110
|
+
if (compose.draft_id) setSelected(null);
|
|
3111
|
+
},
|
|
3112
|
+
onOpenDraft: (id) => {
|
|
3113
|
+
composeClose.current?.();
|
|
3114
|
+
setFolder("drafts");
|
|
3115
|
+
setCursor(null);
|
|
3116
|
+
setSelected(id);
|
|
3117
|
+
}
|
|
3118
|
+
},
|
|
3119
|
+
`${account?.id}-${composeSession}`
|
|
3120
|
+
) : message && selected ? /* @__PURE__ */ jsx(
|
|
3121
|
+
MailMessageView,
|
|
3122
|
+
{
|
|
3123
|
+
account,
|
|
3124
|
+
message,
|
|
3125
|
+
folderRole,
|
|
3126
|
+
folders: folders.data,
|
|
3127
|
+
isOnline,
|
|
3128
|
+
loading: detail.isFetching,
|
|
3129
|
+
uploadPending: savePending,
|
|
3130
|
+
personLabel,
|
|
3131
|
+
renderInvite: renderInvite && (() => renderInvite({
|
|
3132
|
+
accountId: account.id,
|
|
3133
|
+
messageId: message.id,
|
|
3134
|
+
attachments: message.attachments,
|
|
3135
|
+
isOnline
|
|
3136
|
+
})),
|
|
3137
|
+
onBack: () => setSelected(null),
|
|
3138
|
+
onEditDraft: () => void editDraft(),
|
|
3139
|
+
onReply: (forward, all) => void reply(forward, all),
|
|
3140
|
+
onUpdate: (body) => void update(body),
|
|
3141
|
+
onDownloadAttachment: (a) => void downloadAttachment(a),
|
|
3142
|
+
onDownloadOriginal: () => void download(
|
|
3143
|
+
`${accountPath(account.id)}/messages/${message.id}/original`,
|
|
3144
|
+
"original.eml"
|
|
3145
|
+
),
|
|
3146
|
+
onSaveToWorkspace: saveToWorkspace && (() => {
|
|
3147
|
+
setSaveWorkspaceId(
|
|
3148
|
+
saveToWorkspace.defaultWorkspaceId ?? saveToWorkspace.workspaces[0]?.id ?? ""
|
|
3149
|
+
);
|
|
3150
|
+
setSavingToWorkspace(true);
|
|
3151
|
+
})
|
|
3152
|
+
}
|
|
3153
|
+
) : /* @__PURE__ */ jsxs("article", { className: "min-w-0 flex-1 overflow-auto p-4 sm:p-5", children: [
|
|
3154
|
+
selected && /* @__PURE__ */ jsxs(Button, { className: "mb-3", variant: "ghost", onClick: () => setSelected(null), children: [
|
|
3155
|
+
/* @__PURE__ */ jsx(ArrowLeft, { className: "mr-2 size-4" }),
|
|
3156
|
+
"Back"
|
|
3157
|
+
] }),
|
|
3158
|
+
detail.isFetching && selected && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "Loading message\u2026" }),
|
|
3159
|
+
detail.error instanceof MailOfflineUnavailableError && /* @__PURE__ */ jsx("p", { role: "status", className: "text-sm text-muted-foreground", children: detail.error.message })
|
|
3160
|
+
] })
|
|
3161
|
+
})
|
|
3162
|
+
] }),
|
|
3163
|
+
saveToWorkspace && /* @__PURE__ */ jsx(Dialog, { open: savingToWorkspace, onOpenChange: setSavingToWorkspace, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
3164
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Save email to workspace" }),
|
|
3165
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Save a copy as an .eml file. Members with access to the file can read it." }),
|
|
3166
|
+
/* @__PURE__ */ jsxs(
|
|
3167
|
+
"select",
|
|
3168
|
+
{
|
|
3169
|
+
"aria-label": "Destination workspace",
|
|
3170
|
+
value: saveWorkspaceId,
|
|
3171
|
+
onChange: (e) => setSaveWorkspaceId(e.target.value),
|
|
3172
|
+
className: "rounded border bg-background p-2",
|
|
3173
|
+
children: [
|
|
3174
|
+
/* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Choose a workspace" }),
|
|
3175
|
+
saveToWorkspace.workspaces.map((w) => /* @__PURE__ */ jsx("option", { value: w.id, children: w.name }, w.id))
|
|
3176
|
+
]
|
|
3177
|
+
}
|
|
3178
|
+
),
|
|
3179
|
+
/* @__PURE__ */ jsx(Button, { disabled: !saveWorkspaceId || savePending, onClick: () => void saveEmail(), children: savePending ? "Saving\u2026" : "Save email" }),
|
|
3180
|
+
actionError && /* @__PURE__ */ jsx("p", { role: "alert", children: actionError })
|
|
3181
|
+
] }) }),
|
|
3182
|
+
settings && policy && /* @__PURE__ */ jsx(
|
|
3183
|
+
ConnectedMailAccounts,
|
|
3184
|
+
{
|
|
3185
|
+
policy,
|
|
3186
|
+
accounts: accounts.data ?? [],
|
|
3187
|
+
user,
|
|
3188
|
+
oauthReturnPath,
|
|
3189
|
+
settings: offline?.renderSettings?.(),
|
|
3190
|
+
onClose: () => setSettings(false)
|
|
3191
|
+
}
|
|
3192
|
+
)
|
|
3193
|
+
] });
|
|
3194
|
+
}
|
|
1589
3195
|
function ActiveChat({ agentId, workspaceId }) {
|
|
1590
3196
|
const chat = useArbiChat({ agentId, workspaceId });
|
|
1591
3197
|
return /* @__PURE__ */ jsx(
|
|
@@ -1649,6 +3255,6 @@ function ConnectedAgentChatPanel({
|
|
|
1649
3255
|
] });
|
|
1650
3256
|
}
|
|
1651
3257
|
|
|
1652
|
-
export { ArbiAppProvider, ConnectedAgentChatPanel, ConnectedDangerZone, ConnectedIdentityVerification, ConnectedLogoutDialog, ConnectedNotificationPreferences, ConnectedProfileCard, ConnectedRequireVerified, ConnectedSecuritySection, ConnectedSessionsRow, ConnectedSkillUpload,
|
|
3258
|
+
export { ArbiAppProvider, ConnectedAgentChatPanel, ConnectedDangerZone, ConnectedIdentityVerification, ConnectedLogoutDialog, ConnectedMailAccounts, ConnectedMailComposer, ConnectedMailSetup, ConnectedMailbox, ConnectedNotificationPreferences, ConnectedProfileCard, ConnectedRequireVerified, ConnectedSecuritySection, ConnectedSessionsRow, ConnectedSkillUpload, ConnectedTagTable, ConnectedTaskAccessRequest, ConnectedWorkflowRunner, DEFAULT_CONNECTED_WORKFLOW_RUNNER_LABELS, StreamedArtifact, WorkflowRunThread, defaultMailNotify, defaultUnmetPasswordRequirement, resolveMailbox, useConnectedDocxNotifications, useConnectedShellWorkspaces, useConnectedWorkNotifications };
|
|
1653
3259
|
//# sourceMappingURL=index.js.map
|
|
1654
3260
|
//# sourceMappingURL=index.js.map
|