fixes from review
This commit is contained in:
@@ -406,6 +406,7 @@ export function EmailComposer({
|
|||||||
const [smimePassphraseError, setSmimePassphraseError] = useState('');
|
const [smimePassphraseError, setSmimePassphraseError] = useState('');
|
||||||
const [showAttachmentWarning, setShowAttachmentWarning] = useState(false);
|
const [showAttachmentWarning, setShowAttachmentWarning] = useState(false);
|
||||||
const [attachmentWarningKeyword, setAttachmentWarningKeyword] = useState('');
|
const [attachmentWarningKeyword, setAttachmentWarningKeyword] = useState('');
|
||||||
|
const [attachmentWarningDelayedUntil, setAttachmentWarningDelayedUntil] = useState<string | undefined>();
|
||||||
const [showScheduleDialog, setShowScheduleDialog] = useState(false);
|
const [showScheduleDialog, setShowScheduleDialog] = useState(false);
|
||||||
const [scheduleValue, setScheduleValue] = useState('');
|
const [scheduleValue, setScheduleValue] = useState('');
|
||||||
const [scheduleError, setScheduleError] = useState('');
|
const [scheduleError, setScheduleError] = useState('');
|
||||||
@@ -1227,8 +1228,8 @@ export function EmailComposer({
|
|||||||
const time = new Date(value).getTime();
|
const time = new Date(value).getTime();
|
||||||
if (!Number.isFinite(time)) return t('schedule_send_invalid');
|
if (!Number.isFinite(time)) return t('schedule_send_invalid');
|
||||||
if (time <= Date.now()) return t('schedule_send_future');
|
if (time <= Date.now()) return t('schedule_send_future');
|
||||||
if (client) {
|
if (composerClient) {
|
||||||
const maxDelayedSend = client.getMaxDelayedSend();
|
const maxDelayedSend = composerClient.getMaxDelayedSend();
|
||||||
if (maxDelayedSend > 0 && time > Date.now() + maxDelayedSend * 1000) {
|
if (maxDelayedSend > 0 && time > Date.now() + maxDelayedSend * 1000) {
|
||||||
return t('schedule_send_too_late');
|
return t('schedule_send_too_late');
|
||||||
}
|
}
|
||||||
@@ -1239,7 +1240,7 @@ export function EmailComposer({
|
|||||||
const resolveDelayedUntil = async (requestedDelayedUntil?: string): Promise<string | undefined> => {
|
const resolveDelayedUntil = async (requestedDelayedUntil?: string): Promise<string | undefined> => {
|
||||||
if (requestedDelayedUntil) return requestedDelayedUntil;
|
if (requestedDelayedUntil) return requestedDelayedUntil;
|
||||||
if (sendDelaySeconds === 0) return undefined;
|
if (sendDelaySeconds === 0) return undefined;
|
||||||
if (client?.hasDelayedSend()) {
|
if (composerClient?.hasDelayedSend()) {
|
||||||
return new Date(Date.now() + sendDelaySeconds * 1000).toISOString();
|
return new Date(Date.now() + sendDelaySeconds * 1000).toISOString();
|
||||||
}
|
}
|
||||||
const confirmed = window.confirm(t('send_delay_unsupported_confirm'));
|
const confirmed = window.confirm(t('send_delay_unsupported_confirm'));
|
||||||
@@ -1320,6 +1321,7 @@ export function EmailComposer({
|
|||||||
const matched = attachmentReminderKeywords.find(kw => searchText.includes(kw.toLowerCase()));
|
const matched = attachmentReminderKeywords.find(kw => searchText.includes(kw.toLowerCase()));
|
||||||
if (matched) {
|
if (matched) {
|
||||||
setAttachmentWarningKeyword(matched);
|
setAttachmentWarningKeyword(matched);
|
||||||
|
setAttachmentWarningDelayedUntil(delayedUntil);
|
||||||
setShowAttachmentWarning(true);
|
setShowAttachmentWarning(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1654,7 +1656,7 @@ export function EmailComposer({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleScheduleSend = () => {
|
const handleScheduleSend = () => {
|
||||||
if (!client?.hasDelayedSend()) {
|
if (!composerClient?.hasDelayedSend()) {
|
||||||
setScheduleError(t('schedule_send_unsupported'));
|
setScheduleError(t('schedule_send_unsupported'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1742,7 +1744,7 @@ export function EmailComposer({
|
|||||||
|
|
||||||
if (isScheduleShortcut) {
|
if (isScheduleShortcut) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!e.repeat && client?.hasDelayedSend()) openScheduleDialog();
|
if (!e.repeat && composerClient?.hasDelayedSend()) openScheduleDialog();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2225,7 +2227,7 @@ export function EmailComposer({
|
|||||||
>
|
>
|
||||||
{t('discard')}
|
{t('discard')}
|
||||||
</button>
|
</button>
|
||||||
{client?.hasDelayedSend() ? (
|
{composerClient?.hasDelayedSend() ? (
|
||||||
<div ref={sendMenuRef} className="relative hidden md:inline-flex">
|
<div ref={sendMenuRef} className="relative hidden md:inline-flex">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleSend()}
|
onClick={() => handleSend()}
|
||||||
@@ -2412,7 +2414,7 @@ export function EmailComposer({
|
|||||||
<Button variant="outline" onClick={() => setShowAttachmentWarning(false)}>
|
<Button variant="outline" onClick={() => setShowAttachmentWarning(false)}>
|
||||||
{t('forgot_attachment.back')}
|
{t('forgot_attachment.back')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => { setShowAttachmentWarning(false); handleSend(true); }}>
|
<Button onClick={() => { setShowAttachmentWarning(false); handleSend(true, attachmentWarningDelayedUntil); setAttachmentWarningDelayedUntil(undefined); }}>
|
||||||
{t('forgot_attachment.send_anyway')}
|
{t('forgot_attachment.send_anyway')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -471,7 +471,7 @@ export function EmailList({
|
|||||||
onToggleExpand={() => handleToggleThreadExpansion(thread.threadId)}
|
onToggleExpand={() => handleToggleThreadExpansion(thread.threadId)}
|
||||||
onEmailSelect={(email) => onEmailSelect?.(email)}
|
onEmailSelect={(email) => onEmailSelect?.(email)}
|
||||||
onEmailDoubleClick={onEmailDoubleClick ? (email) => onEmailDoubleClick(email) : undefined}
|
onEmailDoubleClick={onEmailDoubleClick ? (email) => onEmailDoubleClick(email) : undefined}
|
||||||
onContextMenu={isScheduledView ? undefined : openContextMenu}
|
onContextMenu={openContextMenu}
|
||||||
onOpenConversation={onOpenConversation}
|
onOpenConversation={onOpenConversation}
|
||||||
onToggleStar={onToggleStar ? (email) => onToggleStar(email) : undefined}
|
onToggleStar={onToggleStar ? (email) => onToggleStar(email) : undefined}
|
||||||
onMarkAsRead={onMarkAsRead ? (email, read) => onMarkAsRead(email, read) : undefined}
|
onMarkAsRead={onMarkAsRead ? (email, read) => onMarkAsRead(email, read) : undefined}
|
||||||
@@ -535,8 +535,8 @@ export function EmailList({
|
|||||||
onMarkAsSpam={() => onMarkAsSpam?.(contextMenu.data!)}
|
onMarkAsSpam={() => onMarkAsSpam?.(contextMenu.data!)}
|
||||||
onUndoSpam={() => onUndoSpam?.(contextMenu.data!)}
|
onUndoSpam={() => onUndoSpam?.(contextMenu.data!)}
|
||||||
onEditDraft={() => onEditDraft?.(contextMenu.data!)}
|
onEditDraft={() => onEditDraft?.(contextMenu.data!)}
|
||||||
onCancelScheduled={isScheduledView ? undefined : () => onCancelScheduled?.(contextMenu.data!)}
|
onCancelScheduled={() => onCancelScheduled?.(contextMenu.data!)}
|
||||||
onCancelScheduledForEdit={isScheduledView ? undefined : () => onCancelScheduledForEdit?.(contextMenu.data!)}
|
onCancelScheduledForEdit={() => onCancelScheduledForEdit?.(contextMenu.data!)}
|
||||||
onRescheduleScheduled={undefined}
|
onRescheduleScheduled={undefined}
|
||||||
onBatchMarkAsRead={(read) => client && batchMarkAsRead(client, read)}
|
onBatchMarkAsRead={(read) => client && batchMarkAsRead(client, read)}
|
||||||
onBatchDelete={() => client && batchDelete(client)}
|
onBatchDelete={() => client && batchDelete(client)}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
|
|||||||
const handleSend = useCallback(async (sendData: Parameters<NonNullable<React.ComponentProps<typeof EmailComposer>['onSend']>>[0]) => {
|
const handleSend = useCallback(async (sendData: Parameters<NonNullable<React.ComponentProps<typeof EmailComposer>['onSend']>>[0]) => {
|
||||||
if (!client) return;
|
if (!client) return;
|
||||||
try {
|
try {
|
||||||
await sendEmail(
|
const result = await sendEmail(
|
||||||
client,
|
client,
|
||||||
sendData.to,
|
sendData.to,
|
||||||
sendData.subject,
|
sendData.subject,
|
||||||
@@ -58,6 +58,11 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
|
|||||||
sendData.envelopeMailFrom,
|
sendData.envelopeMailFrom,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (result.scheduled) {
|
||||||
|
closeTab(tabIdRef.current);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Mark the original message as $answered / $forwarded so the standard
|
// Mark the original message as $answered / $forwarded so the standard
|
||||||
// viewer and list reflect the action (same behaviour as inline compose).
|
// viewer and list reflect the action (same behaviour as inline compose).
|
||||||
if (data.sourceEmailId && (data.mode === 'reply' || data.mode === 'replyAll')) {
|
if (data.sourceEmailId && (data.mode === 'reply' || data.mode === 'replyAll')) {
|
||||||
|
|||||||
@@ -2606,7 +2606,13 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
|||||||
|
|
||||||
cancelUndoSend: async (client, pending) => {
|
cancelUndoSend: async (client, pending) => {
|
||||||
await client.cancelEmailSubmission(pending.submissionId);
|
await client.cancelEmailSubmission(pending.submissionId);
|
||||||
if (pending.emailId && !pending.isSmime) {
|
if (pending.emailId && pending.isSmime) {
|
||||||
|
await client.deleteEmail(pending.emailId);
|
||||||
|
set(state => ({
|
||||||
|
selectedEmail: state.selectedEmail?.id === pending.emailId ? null : state.selectedEmail,
|
||||||
|
selectedEmailIds: new Set(Array.from(state.selectedEmailIds).filter(id => id !== pending.emailId)),
|
||||||
|
}));
|
||||||
|
} else if (pending.emailId) {
|
||||||
const mailboxes = get().mailboxes.length > 0 ? get().mailboxes : await client.getMailboxes();
|
const mailboxes = get().mailboxes.length > 0 ? get().mailboxes : await client.getMailboxes();
|
||||||
const draftsMailbox = mailboxes.find(mb => mb.role === 'drafts');
|
const draftsMailbox = mailboxes.find(mb => mb.role === 'drafts');
|
||||||
const sentMailbox = mailboxes.find(mb => mb.role === 'sent');
|
const sentMailbox = mailboxes.find(mb => mb.role === 'sent');
|
||||||
@@ -2616,7 +2622,7 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
|||||||
}
|
}
|
||||||
await get().refreshScheduledMetadata(client);
|
await get().refreshScheduledMetadata(client);
|
||||||
set({ pendingUndoSend: null });
|
set({ pendingUndoSend: null });
|
||||||
return pending.emailId ? client.getEmail(pending.emailId) : null;
|
return pending.emailId && !pending.isSmime ? client.getEmail(pending.emailId) : null;
|
||||||
},
|
},
|
||||||
|
|
||||||
loadMockData: () => {
|
loadMockData: () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user