@nocobase/app-plugin-mail 1.0.0-beta.10 → 1.0.0-beta.12
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/CHANGELOG.md +36 -0
- package/README.md +10 -2
- package/dist/client/components/mail-account-connector.d.ts +1 -0
- package/dist/client/components/mail-account-connector.d.ts.map +1 -1
- package/dist/client/components/mail-account-connector.js +4 -3
- package/dist/client/components/mail-account-connector.js.map +1 -1
- package/dist/client/components/mail-navigation-icon.d.ts.map +1 -1
- package/dist/client/components/mail-navigation-icon.js +3 -50
- package/dist/client/components/mail-navigation-icon.js.map +1 -1
- package/dist/client/components/public.d.ts +1 -1
- package/dist/client/components/public.d.ts.map +1 -1
- package/dist/client/components/public.js.map +1 -1
- package/dist/client/contracts/composer.d.ts +29 -2
- package/dist/client/contracts/composer.d.ts.map +1 -1
- package/dist/client/contracts/composer.js.map +1 -1
- package/dist/client/hooks/use-mail-composer.d.ts +1 -1
- package/dist/client/hooks/use-mail-composer.d.ts.map +1 -1
- package/dist/client/hooks/use-mail-composer.js +54 -10
- package/dist/client/hooks/use-mail-composer.js.map +1 -1
- package/dist/client/hooks/use-mail-unread-count.d.ts +13 -0
- package/dist/client/hooks/use-mail-unread-count.d.ts.map +1 -0
- package/dist/client/hooks/use-mail-unread-count.js +106 -0
- package/dist/client/hooks/use-mail-unread-count.js.map +1 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/lib/mail-composer-completion.d.ts +5 -0
- package/dist/client/lib/mail-composer-completion.d.ts.map +1 -0
- package/dist/client/lib/mail-composer-completion.js +28 -0
- package/dist/client/lib/mail-composer-completion.js.map +1 -0
- package/dist/client/locales/en-US.d.ts +1 -0
- package/dist/client/locales/en-US.d.ts.map +1 -1
- package/dist/client/locales/en-US.js +4 -3
- package/dist/client/locales/en-US.js.map +1 -1
- package/dist/client/locales/zh-CN.d.ts.map +1 -1
- package/dist/client/locales/zh-CN.js +3 -2
- package/dist/client/locales/zh-CN.js.map +1 -1
- package/dist/client/pages/mail-accounts-page.d.ts.map +1 -1
- package/dist/client/pages/mail-accounts-page.js +4 -1
- package/dist/client/pages/mail-accounts-page.js.map +1 -1
- package/dist/client/pages/mail-workspace-page.d.ts +4 -2
- package/dist/client/pages/mail-workspace-page.d.ts.map +1 -1
- package/dist/client/pages/mail-workspace-page.js +222 -171
- package/dist/client/pages/mail-workspace-page.js.map +1 -1
- package/dist/client/prepare-mail-reply.d.ts +9 -0
- package/dist/client/prepare-mail-reply.d.ts.map +1 -0
- package/dist/client/prepare-mail-reply.js +28 -0
- package/dist/client/prepare-mail-reply.js.map +1 -0
- package/dist/server/config.d.ts +1 -1
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +17 -9
- package/dist/server/config.js.map +1 -1
- package/dist/server/locales/en-US.d.ts +1 -0
- package/dist/server/locales/en-US.d.ts.map +1 -1
- package/dist/server/locales/en-US.js +1 -0
- package/dist/server/locales/en-US.js.map +1 -1
- package/dist/server/locales/zh-CN.d.ts.map +1 -1
- package/dist/server/locales/zh-CN.js +1 -0
- package/dist/server/locales/zh-CN.js.map +1 -1
- package/dist/server/providers/mail-core.d.ts +2 -0
- package/dist/server/providers/mail-core.d.ts.map +1 -1
- package/dist/server/providers/mail-core.js +39 -1
- package/dist/server/providers/mail-core.js.map +1 -1
- package/dist/server/routes/api.js +3 -1
- package/dist/server/routes/api.js.map +1 -1
- package/dist/server/services/authorization.js +1 -1
- package/dist/server/services/authorization.js.map +1 -1
- package/dist/shared/mail.d.ts +1 -1
- package/dist/shared/mail.d.ts.map +1 -1
- package/package.json +15 -12
- package/skills/nocobase-app-plugin-mail/references/client-integration.md +106 -0
- package/skills/nocobase-app-plugin-mail/references/configuration-and-accounts.md +10 -4
- package/skills/nocobase-app-plugin-mail/references/public-api.md +25 -20
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { PageContainer } from '../components/page-container.js';
|
|
3
3
|
import { PageHeader } from '../components/page-header.js';
|
|
4
|
-
import {
|
|
4
|
+
import { prepareMailReply } from '../prepare-mail-reply.js';
|
|
5
5
|
import { mailErrorDescription } from '../lib/mail-error-description.js';
|
|
6
6
|
import { useMailWorkspaceData } from '../hooks/use-mail-workspace-data.js';
|
|
7
7
|
import { createForwardQuote, readDraftComposerBody, } from '../lib/mail-forward-content.js';
|
|
8
8
|
import { EMPTY_COMPOSER, } from '../lib/mail-composer-state.js';
|
|
9
|
-
import { ChevronLeft, ChevronRight, Inbox, PenLine, RefreshCw, Search, } from 'lucide-react';
|
|
9
|
+
import { ChevronLeft, ChevronRight, Mailbox, Inbox, PenLine, RefreshCw, Search, } from 'lucide-react';
|
|
10
10
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
11
11
|
import { useTranslation } from '@nocobase/i18n/client';
|
|
12
12
|
import { resolveAppUrl } from '@nocobase/app-client';
|
|
@@ -14,6 +14,8 @@ import { MailWorkspaceComposer } from '../components/mail-workspace-composer.js'
|
|
|
14
14
|
import { MailboxSidebar, MailConversationView, MailMessageList, MAIL_UNREAD_COUNT_CHANGED_EVENT, } from '../components/index.js';
|
|
15
15
|
import { Button } from '../components/ui/button.js';
|
|
16
16
|
import { Input } from '../components/ui/input.js';
|
|
17
|
+
import { Sheet, SheetContent, SheetHeader, SheetTitle, } from '../components/ui/sheet.js';
|
|
18
|
+
import { cn } from '../lib/utils.js';
|
|
17
19
|
import { getMailFolderDisplayName, getMailFolderSections, getVirtualMailFolders, } from '../lib/mail-folders.js';
|
|
18
20
|
import { mailErrorMessage, } from '../mail-client.js';
|
|
19
21
|
import { useMailClient } from '../runtime.js';
|
|
@@ -21,7 +23,7 @@ import { mailSyncDateMonthsAgo } from '../../shared/mail-sync-date.js';
|
|
|
21
23
|
import { MAIL_VIRTUAL_FOLDER_IDS } from '../../shared/mail.js';
|
|
22
24
|
import { MAIL_PLUGIN_NS } from '../namespace.js';
|
|
23
25
|
const LAST_COMPOSE_ACCOUNT_KEY_PREFIX = 'nocobase:mail:last-compose-account:v1:';
|
|
24
|
-
export default function MailWorkspacePage({ title, description, templateVariables = {},
|
|
26
|
+
export default function MailWorkspacePage({ title, description, accountsHref, headerActions, templateVariables = {}, } = {}) {
|
|
25
27
|
const { t } = useTranslation(MAIL_PLUGIN_NS);
|
|
26
28
|
const mail = useMailClient();
|
|
27
29
|
const [accounts, setAccounts] = useState([]);
|
|
@@ -46,6 +48,24 @@ export default function MailWorkspacePage({ title, description, templateVariable
|
|
|
46
48
|
const [debouncedQuery, setDebouncedQuery] = useState('');
|
|
47
49
|
const [composeAccountId, setComposeAccountId] = useState('');
|
|
48
50
|
const [composerRequest, setComposerRequest] = useState();
|
|
51
|
+
const [navigationOpen, setNavigationOpen] = useState(false);
|
|
52
|
+
const workspaceRef = useRef(null);
|
|
53
|
+
const navigationButtonRef = useRef(null);
|
|
54
|
+
const conversationBackRef = useRef(null);
|
|
55
|
+
const selectedRowRef = useRef(null);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!navigationOpen ||
|
|
58
|
+
!navigationButtonRef.current ||
|
|
59
|
+
typeof ResizeObserver === 'undefined')
|
|
60
|
+
return;
|
|
61
|
+
// CSS owns the breakpoint; observing its trigger also handles root font changes.
|
|
62
|
+
const observer = new ResizeObserver(() => {
|
|
63
|
+
if (!navigationButtonRef.current?.getClientRects().length)
|
|
64
|
+
setNavigationOpen(false);
|
|
65
|
+
});
|
|
66
|
+
observer.observe(navigationButtonRef.current);
|
|
67
|
+
return () => observer.disconnect();
|
|
68
|
+
}, [navigationOpen]);
|
|
49
69
|
useEffect(() => {
|
|
50
70
|
syncRunsRef.current = syncRuns;
|
|
51
71
|
}, [syncRuns]);
|
|
@@ -70,6 +90,14 @@ export default function MailWorkspacePage({ title, description, templateVariable
|
|
|
70
90
|
setError,
|
|
71
91
|
onFocus: () => loadAccounts(false),
|
|
72
92
|
});
|
|
93
|
+
const selectedMessageKey = selected
|
|
94
|
+
? JSON.stringify([selected.accountId, selected.id])
|
|
95
|
+
: undefined;
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (selectedMessageKey &&
|
|
98
|
+
navigationButtonRef.current?.getClientRects().length)
|
|
99
|
+
conversationBackRef.current?.focus({ preventScroll: true });
|
|
100
|
+
}, [selectedMessageKey]);
|
|
73
101
|
const currentAccount = accounts.find((account) => account.id === accountId);
|
|
74
102
|
const currentProviderCapabilities = findProviderCapabilities(currentAccount, providers);
|
|
75
103
|
const composeAccount = accounts.find((account) => account.id === composeAccountId);
|
|
@@ -436,6 +464,11 @@ export default function MailWorkspacePage({ title, description, templateVariable
|
|
|
436
464
|
const syncLabel = isAllAccounts
|
|
437
465
|
? t('workspace.syncAll', { defaultValue: 'Sync all mailboxes' })
|
|
438
466
|
: t('workspace.incrementalRefresh', { defaultValue: 'Sync mailbox' });
|
|
467
|
+
const closeMailboxNavigation = () => {
|
|
468
|
+
setNavigationOpen(false);
|
|
469
|
+
if (navigationOpen)
|
|
470
|
+
clearSelection();
|
|
471
|
+
};
|
|
439
472
|
const mailboxNavigation = (_jsx(MailboxSidebar, { accountId: accountId, accounts: accounts, customLabels: customLabels, folderId: folderId, folderAccountId: folderAccountId, folders: folders, accountFolderGroups: accountFolderGroups, labelId: labelId, labels: {
|
|
440
473
|
account: t('dev.account', { defaultValue: 'Account' }),
|
|
441
474
|
allAccounts: t('workspace.allAccounts', {
|
|
@@ -449,6 +482,7 @@ export default function MailWorkspacePage({ title, description, templateVariable
|
|
|
449
482
|
folderNames,
|
|
450
483
|
systemFolderNames,
|
|
451
484
|
}, onAccountChange: (value) => {
|
|
485
|
+
closeMailboxNavigation();
|
|
452
486
|
if (value === accountId)
|
|
453
487
|
return;
|
|
454
488
|
cancelRequests();
|
|
@@ -462,6 +496,7 @@ export default function MailWorkspacePage({ title, description, templateVariable
|
|
|
462
496
|
setLabelId(undefined);
|
|
463
497
|
setSmartView('all');
|
|
464
498
|
}, onFolderChange: (value, ownerAccountId) => {
|
|
499
|
+
closeMailboxNavigation();
|
|
465
500
|
if (value === folderId && ownerAccountId === folderAccountId)
|
|
466
501
|
return;
|
|
467
502
|
cancelRequests();
|
|
@@ -470,6 +505,7 @@ export default function MailWorkspacePage({ title, description, templateVariable
|
|
|
470
505
|
setLabelId(undefined);
|
|
471
506
|
setSmartView('all');
|
|
472
507
|
}, onLabelChange: (value) => {
|
|
508
|
+
closeMailboxNavigation();
|
|
473
509
|
if (value === labelId)
|
|
474
510
|
return;
|
|
475
511
|
cancelRequests();
|
|
@@ -478,6 +514,7 @@ export default function MailWorkspacePage({ title, description, templateVariable
|
|
|
478
514
|
setFolderAccountId(undefined);
|
|
479
515
|
setSmartView('all');
|
|
480
516
|
}, onSmartViewChange: (value) => {
|
|
517
|
+
closeMailboxNavigation();
|
|
481
518
|
if (value === smartView)
|
|
482
519
|
return;
|
|
483
520
|
cancelRequests();
|
|
@@ -513,11 +550,13 @@ export default function MailWorkspacePage({ title, description, templateVariable
|
|
|
513
550
|
? t('workspace.syncing', {
|
|
514
551
|
defaultValue: 'Synchronizing…',
|
|
515
552
|
})
|
|
516
|
-
: syncLabel })] })] }) }), _jsxs("div", { className: 'flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden rounded-xl border bg-background', children: [
|
|
517
|
-
defaultValue: '
|
|
518
|
-
}), className: '
|
|
519
|
-
|
|
520
|
-
|
|
553
|
+
: syncLabel })] }), accountsHref && accounts.length > 0 ? (_jsx(Button, { render: _jsx("a", { href: resolveAppUrl(accountsHref) }), nativeButton: false, role: 'link', variant: 'outline', children: t('navigation.accounts', { defaultValue: 'Mail accounts' }) })) : null, headerActions] }) }), _jsxs("div", { ref: workspaceRef, "data-slot": 'mail-workspace', className: '@container/mail flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden rounded-xl border bg-background', children: [_jsxs("div", { className: 'flex shrink-0 flex-wrap items-center gap-2 border-b p-4', children: [accounts.length > 0 ? (_jsxs(Button, { ref: navigationButtonRef, variant: 'outline', className: '@min-[56rem]/mail:hidden', "aria-haspopup": 'dialog', "aria-expanded": navigationOpen, onClick: () => setNavigationOpen(true), children: [_jsx(Mailbox, { "aria-hidden": 'true', className: 'size-4' }), t('workspace.mailboxNavigation', {
|
|
554
|
+
defaultValue: 'Mailboxes and folders',
|
|
555
|
+
})] })) : null, _jsxs("label", { className: 'relative block w-full min-w-0 @min-[32rem]/mail:w-64', children: [_jsx(Search, { "aria-hidden": 'true', className: 'absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground' }), _jsx(Input, { "aria-label": t('workspace.search', {
|
|
556
|
+
defaultValue: 'Search mail',
|
|
557
|
+
}), className: 'pl-9', onChange: (event) => setQuery(event.target.value), placeholder: t('workspace.search', {
|
|
558
|
+
defaultValue: 'Search mail',
|
|
559
|
+
}), value: query })] })] }), error ? (_jsxs("div", { role: 'alert', className: 'flex items-center gap-3 border-b bg-destructive/5 px-4 py-2 text-sm text-destructive', children: [_jsx("span", { className: 'flex-1', children: error }), _jsx(Button, { onClick: () => {
|
|
521
560
|
loadAccounts();
|
|
522
561
|
setReloadVersion((version) => version + 1);
|
|
523
562
|
}, variant: 'ghost', children: t('workspace.retry', { defaultValue: 'Try again' }) })] })) : null, notice ? (_jsxs("div", { role: 'status', className: 'flex items-center gap-3 border-b bg-primary/5 px-4 py-2 text-sm', children: [_jsx("span", { className: 'flex-1', children: notice }), _jsx(Button, { variant: 'ghost', onClick: () => setNotice(undefined), children: t('workspace.dismiss', { defaultValue: 'Dismiss' }) })] })) : null, loadingAccounts && accounts.length === 0 ? (_jsxs("div", { role: 'status', className: 'flex min-h-64 flex-1 items-center justify-center gap-2 text-sm text-muted-foreground', children: [_jsx(RefreshCw, { "aria-hidden": 'true', className: 'size-4 animate-spin' }), t('workspace.loading', { defaultValue: 'Loading messages…' })] })) : accounts.length === 0 ? (_jsx("div", { className: 'grid flex-1 place-items-center p-8', children: _jsxs("div", { className: 'max-w-md space-y-4 text-center', children: [_jsx("span", { className: 'mx-auto grid size-14 place-items-center rounded-2xl bg-muted', children: _jsx(Inbox, { "aria-hidden": 'true', className: 'size-6 text-muted-foreground' }) }), _jsxs("div", { children: [_jsx("h2", { className: 'text-lg font-semibold text-foreground', children: t('workspace.noAccountsTitle', {
|
|
@@ -526,7 +565,14 @@ export default function MailWorkspacePage({ title, description, templateVariable
|
|
|
526
565
|
defaultValue: 'Connect a mail account to sync messages, search conversations, and send from this workspace.',
|
|
527
566
|
}) })] }), accountsHref ? (_jsxs(Button, { render: _jsx("a", { href: resolveAppUrl(accountsHref) }), nativeButton: false, role: 'link', children: [_jsx(PenLine, { "aria-hidden": 'true', className: 'size-4' }), t('workspace.connectAccount', {
|
|
528
567
|
defaultValue: 'Connect mail account',
|
|
529
|
-
})] })) : null] }) })) : (_jsxs("div", { className: 'grid min-h-0 flex-1 grid-cols-[13rem_20rem_minmax(0,1fr)]
|
|
568
|
+
})] })) : null] }) })) : (_jsxs("div", { className: 'grid min-h-0 min-w-0 flex-1 grid-cols-1 overflow-hidden @min-[56rem]/mail:grid-cols-[13rem_20rem_minmax(0,1fr)]', children: [_jsx("div", { "data-slot": 'mail-navigation-pane', className: 'hidden min-h-0 @min-[56rem]/mail:block', children: mailboxNavigation }), _jsxs("div", { "data-slot": 'mail-list-pane', onClickCapture: (event) => {
|
|
569
|
+
if (!(event.target instanceof Element))
|
|
570
|
+
return;
|
|
571
|
+
const row = event.target.closest('button');
|
|
572
|
+
const list = event.currentTarget.querySelector('section');
|
|
573
|
+
if (row instanceof HTMLButtonElement && list?.contains(row))
|
|
574
|
+
selectedRowRef.current = row;
|
|
575
|
+
}, className: cn('min-h-0 min-w-0 flex-col @min-[56rem]/mail:flex @min-[56rem]/mail:border-r', selected ? 'hidden' : 'flex'), children: [_jsx("div", { className: 'flex shrink-0 items-center justify-between gap-3 border-b bg-muted/20 px-4 py-3', children: _jsx("h2", { className: 'truncate text-sm font-semibold', children: viewTitle }) }), _jsx(MailMessageList, { accountNames: accountNames, availableLabels: customLabels, labels: {
|
|
530
576
|
loading: t('workspace.loading', {
|
|
531
577
|
defaultValue: 'Loading messages…',
|
|
532
578
|
}),
|
|
@@ -569,168 +615,176 @@ export default function MailWorkspacePage({ title, description, templateVariable
|
|
|
569
615
|
defaultValue: 'Next page',
|
|
570
616
|
}), title: t('workspace.nextPage', {
|
|
571
617
|
defaultValue: 'Next page',
|
|
572
|
-
}), type: 'button', onClick: () => changeMessagePage(pageIndex + 1), children: _jsx(ChevronRight, { "aria-hidden": 'true', className: 'size-3.5' }) })] })] })] }),
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
});
|
|
620
|
-
},
|
|
621
|
-
forward: (message) => openComposer({
|
|
622
|
-
...EMPTY_COMPOSER,
|
|
623
|
-
mode: 'forward',
|
|
624
|
-
relatedMessageId: message.id,
|
|
625
|
-
subject: forwardSubject(message.subject),
|
|
626
|
-
forwardQuote: createForwardQuote(message),
|
|
627
|
-
forwardBodyIncluded: true,
|
|
628
|
-
}, [], message.accountId),
|
|
629
|
-
cancelScheduled: (message) => editScheduledDraft(message, () => { }),
|
|
630
|
-
editDraft: selectedMessageCanDraft
|
|
631
|
-
? (message) => editScheduledDraft(message, (message) => openComposer({
|
|
618
|
+
}), type: 'button', onClick: () => changeMessagePage(pageIndex + 1), children: _jsx(ChevronRight, { "aria-hidden": 'true', className: 'size-3.5' }) })] })] })] }), _jsxs("div", { "data-slot": 'mail-conversation-pane', className: cn('min-h-0 min-w-0 flex-col @min-[56rem]/mail:flex', selected ? 'flex' : 'hidden'), children: [_jsxs(Button, { ref: conversationBackRef, variant: 'ghost', className: 'm-2 shrink-0 self-start @min-[56rem]/mail:hidden', onClick: () => {
|
|
619
|
+
clearSelection();
|
|
620
|
+
window.requestAnimationFrame(() => {
|
|
621
|
+
if (selectedRowRef.current?.isConnected)
|
|
622
|
+
selectedRowRef.current.focus({ preventScroll: true });
|
|
623
|
+
});
|
|
624
|
+
}, children: [_jsx(ChevronLeft, { "aria-hidden": 'true', className: 'size-4' }), t('workspace.backToMessages', {
|
|
625
|
+
defaultValue: 'Back to message list',
|
|
626
|
+
})] }), _jsx(MailConversationView, { availableLabels: selectedMessageCanUseLabels ? customLabels : [], actions: selectedMessageAccount?.status === 'active'
|
|
627
|
+
? {
|
|
628
|
+
archive: selectedMessageCanMove
|
|
629
|
+
? (message) => {
|
|
630
|
+
const archive = foldersByAccountId
|
|
631
|
+
.get(message.accountId)
|
|
632
|
+
?.find((folder) => folder.type === 'archive');
|
|
633
|
+
if (archive)
|
|
634
|
+
mutateMessage(mail.moveMessage({
|
|
635
|
+
accountId: message.accountId,
|
|
636
|
+
messageId: message.id,
|
|
637
|
+
providerFolderId: archive.providerFolderId,
|
|
638
|
+
}), true);
|
|
639
|
+
}
|
|
640
|
+
: undefined,
|
|
641
|
+
delete: (message) => deleteWorkspaceMessage(message),
|
|
642
|
+
canDelete: (message) => Boolean(!message.scheduledSend &&
|
|
643
|
+
(selectedMessageProviderCapabilities?.moveMessage ||
|
|
644
|
+
isMessageInTrash(message, foldersByAccountId) ||
|
|
645
|
+
message.providerMessageId.startsWith('local-draft:'))),
|
|
646
|
+
downloadAttachment: (message, attachment) => {
|
|
647
|
+
void downloadAttachment(mail, message, attachment).catch((cause) => setError(mailErrorMessage(cause, t('workspace.downloadFailed', {
|
|
648
|
+
defaultValue: 'Unable to download attachment.',
|
|
649
|
+
}))));
|
|
650
|
+
},
|
|
651
|
+
reply: (message) => {
|
|
652
|
+
if (preparingReplyRef.current || composerRequest)
|
|
653
|
+
return;
|
|
654
|
+
preparingReplyRef.current = true;
|
|
655
|
+
void prepareMailReply(mail, message)
|
|
656
|
+
.then((reply) => {
|
|
657
|
+
openComposer(reply.value, reply.attachments, reply.accountId, reply.uploads);
|
|
658
|
+
})
|
|
659
|
+
.catch(requestError)
|
|
660
|
+
.finally(() => {
|
|
661
|
+
preparingReplyRef.current = false;
|
|
662
|
+
});
|
|
663
|
+
},
|
|
664
|
+
forward: (message) => openComposer({
|
|
632
665
|
...EMPTY_COMPOSER,
|
|
633
|
-
mode: '
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
accountId: message.accountId,
|
|
659
|
-
messageId: message.id,
|
|
660
|
-
note: note.trim() || null,
|
|
661
|
-
})),
|
|
662
|
-
toggleLabel: selectedMessageCanUseLabels
|
|
663
|
-
? (message, labelId, assigned) => mutateMessage(mail.updateMessageLabels({
|
|
666
|
+
mode: 'forward',
|
|
667
|
+
relatedMessageId: message.id,
|
|
668
|
+
subject: forwardSubject(message.subject),
|
|
669
|
+
forwardQuote: createForwardQuote(message),
|
|
670
|
+
forwardBodyIncluded: true,
|
|
671
|
+
}, [], message.accountId),
|
|
672
|
+
cancelScheduled: (message) => editScheduledDraft(message, () => { }),
|
|
673
|
+
editDraft: selectedMessageCanDraft
|
|
674
|
+
? (message) => editScheduledDraft(message, (message) => openComposer({
|
|
675
|
+
...EMPTY_COMPOSER,
|
|
676
|
+
mode: 'edit',
|
|
677
|
+
draftMessageId: message.id,
|
|
678
|
+
draftRevision: message.draftRevision,
|
|
679
|
+
draftSource: message.draftSource,
|
|
680
|
+
fromAddress: message.from?.address,
|
|
681
|
+
to: formatAddressList(message.to),
|
|
682
|
+
cc: formatAddressList(message.cc),
|
|
683
|
+
bcc: formatAddressList(message.bcc),
|
|
684
|
+
subject: message.subject,
|
|
685
|
+
...readDraftComposerBody(message),
|
|
686
|
+
draftConflict: message.draftConflict,
|
|
687
|
+
}, message.attachments, message.accountId))
|
|
688
|
+
: undefined,
|
|
689
|
+
toggleRead: (message) => mutateMessage(setMessageRead(message, !message.read)),
|
|
690
|
+
toggleStarred: (message) => mutateMessage(mail.updateMessage({
|
|
664
691
|
accountId: message.accountId,
|
|
665
692
|
messageId: message.id,
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
defaultValue: '
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
693
|
+
starred: !message.starred,
|
|
694
|
+
})),
|
|
695
|
+
toggleTodo: (message) => mutateMessage(mail.updateMessage({
|
|
696
|
+
accountId: message.accountId,
|
|
697
|
+
messageId: message.id,
|
|
698
|
+
todo: !message.todo,
|
|
699
|
+
})),
|
|
700
|
+
saveNote: (message, note) => mutateMessage(mail.updateMessage({
|
|
701
|
+
accountId: message.accountId,
|
|
702
|
+
messageId: message.id,
|
|
703
|
+
note: note.trim() || null,
|
|
704
|
+
})),
|
|
705
|
+
toggleLabel: selectedMessageCanUseLabels
|
|
706
|
+
? (message, labelId, assigned) => mutateMessage(mail.updateMessageLabels({
|
|
707
|
+
accountId: message.accountId,
|
|
708
|
+
messageId: message.id,
|
|
709
|
+
addLabelIds: assigned ? [labelId] : [],
|
|
710
|
+
removeLabelIds: assigned ? [] : [labelId],
|
|
711
|
+
}))
|
|
712
|
+
: undefined,
|
|
713
|
+
}
|
|
714
|
+
: undefined, actionLabels: {
|
|
715
|
+
archive: t('workspace.archive', { defaultValue: 'Archive' }),
|
|
716
|
+
delete: selectedMessageInTrash
|
|
717
|
+
? t('workspace.permanentlyDelete', {
|
|
718
|
+
defaultValue: 'Permanently delete',
|
|
719
|
+
})
|
|
720
|
+
: t('workspace.delete', { defaultValue: 'Delete' }),
|
|
721
|
+
download: t('workspace.download', {
|
|
722
|
+
defaultValue: 'Download',
|
|
723
|
+
}),
|
|
724
|
+
reply: t('workspace.reply', { defaultValue: 'Reply' }),
|
|
725
|
+
forward: t('workspace.forward', { defaultValue: 'Forward' }),
|
|
726
|
+
editDraft: t('workspace.editDraft', {
|
|
727
|
+
defaultValue: 'Edit draft',
|
|
728
|
+
}),
|
|
729
|
+
markRead: t('workspace.markRead', {
|
|
730
|
+
defaultValue: 'Mark read',
|
|
731
|
+
}),
|
|
732
|
+
markUnread: t('workspace.markUnread', {
|
|
733
|
+
defaultValue: 'Mark unread',
|
|
734
|
+
}),
|
|
735
|
+
star: t('workspace.star', { defaultValue: 'Star' }),
|
|
736
|
+
unstar: t('workspace.unstar', {
|
|
737
|
+
defaultValue: 'Remove star',
|
|
738
|
+
}),
|
|
739
|
+
collapseMessage: t('workspace.collapseMessage', {
|
|
740
|
+
defaultValue: 'Collapse message',
|
|
741
|
+
}),
|
|
742
|
+
expandMessage: t('workspace.expandMessage', {
|
|
743
|
+
defaultValue: 'Expand message',
|
|
744
|
+
}),
|
|
745
|
+
}, labels: {
|
|
746
|
+
attachmentCount: (count) => t('workspace.attachmentCount', {
|
|
747
|
+
count,
|
|
748
|
+
defaultValue: '{{count}} attachments',
|
|
749
|
+
}),
|
|
750
|
+
conversation: (count) => t('workspace.conversation', {
|
|
751
|
+
count,
|
|
752
|
+
defaultValue: '{{count}} messages in this conversation',
|
|
753
|
+
}),
|
|
754
|
+
loadMore: t('workspace.loadEarlier', {
|
|
755
|
+
defaultValue: 'Load earlier messages',
|
|
756
|
+
}),
|
|
757
|
+
noSubject: t('workspace.noSubject', {
|
|
758
|
+
defaultValue: '(no subject)',
|
|
759
|
+
}),
|
|
760
|
+
selectMessage: t('workspace.selectMessage', {
|
|
761
|
+
defaultValue: 'Select a message to read it.',
|
|
762
|
+
}),
|
|
763
|
+
unknownSender: t('workspace.unknownSender', {
|
|
764
|
+
defaultValue: 'Unknown sender',
|
|
765
|
+
}),
|
|
766
|
+
labels: t('workspace.labels', { defaultValue: 'Labels' }),
|
|
767
|
+
note: t('workspace.note', { defaultValue: 'Note' }),
|
|
768
|
+
notePlaceholder: t('workspace.notePlaceholder', {
|
|
769
|
+
defaultValue: 'Add a private note…',
|
|
770
|
+
}),
|
|
771
|
+
saveNote: t('workspace.saveNote', {
|
|
772
|
+
defaultValue: 'Save note',
|
|
773
|
+
}),
|
|
774
|
+
todo: t('workspace.todo', { defaultValue: 'To do' }),
|
|
775
|
+
more: t('workspace.more', { defaultValue: 'More actions' }),
|
|
776
|
+
}, loading: loadingConversation, messages: conversation, nextCursor: conversationCursor, onLoadMore: loadMoreConversation, subject: selected?.subject })] })] })), _jsx(Sheet, { open: navigationOpen, onOpenChange: setNavigationOpen, children: _jsxs(SheetContent, { side: 'left', closeLabel: t('workspace.closeNavigation', {
|
|
777
|
+
defaultValue: 'Close mailbox navigation',
|
|
778
|
+
}), finalFocus: () => navigationButtonRef.current?.getClientRects().length
|
|
779
|
+
? navigationButtonRef.current
|
|
780
|
+
: workspaceRef.current?.querySelector('input'), children: [_jsx(SheetHeader, { className: 'pr-14', children: _jsx(SheetTitle, { children: t('workspace.mailboxNavigation', {
|
|
781
|
+
defaultValue: 'Mailboxes and folders',
|
|
782
|
+
}) }) }), _jsx("div", { className: 'min-h-0 flex-1 overflow-y-auto', onClickCapture: (event) => {
|
|
783
|
+
// Current folders can return early inside the reused sidebar itself.
|
|
784
|
+
if (event.target instanceof Element &&
|
|
785
|
+
event.target.closest('button[aria-current="page"]'))
|
|
786
|
+
closeMailboxNavigation();
|
|
787
|
+
}, children: mailboxNavigation })] }) }), composerRequest ? (_jsx(MailWorkspaceComposer, { onSelectAccount: rememberComposeAccount, request: composerRequest, accounts: accounts, providers: providers, templateVariables: templateVariables, onClose: () => setComposerRequest(undefined), onComplete: (result, rejectedRecipients, operationError) => {
|
|
734
788
|
setReloadVersion((version) => version + 1);
|
|
735
789
|
const notice = result === 'partial'
|
|
736
790
|
? t('workspace.submissionPartial', {
|
|
@@ -813,9 +867,6 @@ function isMessageInTrash(message, foldersByAccountId) {
|
|
|
813
867
|
function formatAddressList(addresses) {
|
|
814
868
|
return addresses.map((address) => address.address).join(', ');
|
|
815
869
|
}
|
|
816
|
-
function replySubject(subject) {
|
|
817
|
-
return /^re:/iu.test(subject.trim()) ? subject : `Re: ${subject}`;
|
|
818
|
-
}
|
|
819
870
|
function forwardSubject(subject) {
|
|
820
871
|
return /^fwd?:/iu.test(subject.trim()) ? subject : `Fwd: ${subject}`;
|
|
821
872
|
}
|