fix
This commit is contained in:
@@ -72,7 +72,7 @@ interface EmailComposerProps {
|
||||
attachments?: Array<{ blobId: string; name: string; type: string; size: number; disposition?: 'attachment' | 'inline'; cid?: string }>;
|
||||
inReplyTo?: string[];
|
||||
references?: string[];
|
||||
sendAt?: string;
|
||||
delayedUntil?: string;
|
||||
}) => void | Promise<void>;
|
||||
onScheduledSendCreated?: () => void | Promise<void>;
|
||||
onClose?: () => void;
|
||||
@@ -876,8 +876,8 @@ export function EmailComposer({
|
||||
return null;
|
||||
};
|
||||
|
||||
const getEffectiveSendAt = async (explicitSendAt?: string): Promise<string | undefined> => {
|
||||
if (explicitSendAt) return explicitSendAt;
|
||||
const resolveDelayedUntil = async (requestedDelayedUntil?: string): Promise<string | undefined> => {
|
||||
if (requestedDelayedUntil) return requestedDelayedUntil;
|
||||
if (sendDelaySeconds === 0) return undefined;
|
||||
if (client?.hasDelayedSend()) {
|
||||
return new Date(Date.now() + sendDelaySeconds * 1000).toISOString();
|
||||
@@ -932,7 +932,7 @@ export function EmailComposer({
|
||||
};
|
||||
};
|
||||
|
||||
const handleSend = async (skipAttachmentCheck = false, sendAt?: string) => {
|
||||
const handleSend = async (skipAttachmentCheck = false, delayedUntil?: string) => {
|
||||
const ccAddresses = cc.split(",").map(e => e.trim()).filter(Boolean);
|
||||
const bccAddresses = bcc.split(",").map(e => e.trim()).filter(Boolean);
|
||||
|
||||
@@ -1014,7 +1014,7 @@ export function EmailComposer({
|
||||
const inlineAttachments = rewritten?.attachments ?? [];
|
||||
|
||||
try {
|
||||
const effectiveSendAt = await getEffectiveSendAt(sendAt);
|
||||
const effectiveDelayedUntil = await resolveDelayedUntil(delayedUntil);
|
||||
// Let plugins veto the send (external-mail warning, mistyped-domain
|
||||
// guards, etc.). Returning false from any handler aborts before either
|
||||
// the S/MIME or standard JMAP path runs.
|
||||
@@ -1151,8 +1151,8 @@ export function EmailComposer({
|
||||
}
|
||||
|
||||
// 7. Send via raw email path
|
||||
const result = await sendRawEmail(client, payload, currentIdentity.id, effectiveSendAt);
|
||||
if (effectiveSendAt && finalDraftId) {
|
||||
const result = await sendRawEmail(client, payload, currentIdentity.id, effectiveDelayedUntil);
|
||||
if (effectiveDelayedUntil && finalDraftId) {
|
||||
client.deleteEmail(finalDraftId).catch(err => {
|
||||
debug.warn('email', 'Scheduled S/MIME send created, but plaintext draft cleanup failed:', err);
|
||||
toast.warning(t('schedule_send_cleanup_warning'));
|
||||
@@ -1199,7 +1199,7 @@ export function EmailComposer({
|
||||
attachments: uploadedAttachments.length > 0 ? uploadedAttachments : undefined,
|
||||
inReplyTo: threadingHeaders?.inReplyTo,
|
||||
references: threadingHeaders?.references,
|
||||
sendAt: effectiveSendAt,
|
||||
delayedUntil: effectiveDelayedUntil,
|
||||
});
|
||||
|
||||
if (mode === 'reply' || mode === 'replyAll') {
|
||||
@@ -1700,20 +1700,21 @@ export function EmailComposer({
|
||||
>
|
||||
<BookmarkPlus className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
setScheduleError('');
|
||||
setScheduleValue('');
|
||||
setShowScheduleDialog(true);
|
||||
}}
|
||||
disabled={!client?.hasDelayedSend()}
|
||||
title={client?.hasDelayedSend() ? t('schedule_send') : t('schedule_send_unsupported')}
|
||||
className="h-9 w-9"
|
||||
>
|
||||
<CalendarClock className="w-4 h-4" />
|
||||
</Button>
|
||||
{client?.hasDelayedSend() && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
setScheduleError('');
|
||||
setScheduleValue('');
|
||||
setShowScheduleDialog(true);
|
||||
}}
|
||||
title={t('schedule_send')}
|
||||
className="h-9 w-9"
|
||||
>
|
||||
<CalendarClock className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* S/MIME toggles */}
|
||||
{canSmimeSign && (
|
||||
|
||||
@@ -43,7 +43,7 @@ interface EmailListProps {
|
||||
onLoadMoreScheduled?: () => void;
|
||||
onCancelScheduled?: (email: Email) => void | Promise<void>;
|
||||
onCancelScheduledForEdit?: (email: Email) => void | Promise<void>;
|
||||
onRescheduleScheduled?: (email: Email, sendAt: string) => void | Promise<void>;
|
||||
onRescheduleScheduled?: (email: Email, delayedUntil: string) => void | Promise<void>;
|
||||
}
|
||||
|
||||
export function EmailList({
|
||||
@@ -235,7 +235,7 @@ export function EmailList({
|
||||
}
|
||||
}, [client, hasMoreEmails, isLoadingMore, isLoading, isScheduledView, loadMoreEmails, onLoadMoreScheduled]);
|
||||
|
||||
const promptForRescheduleSendAt = useCallback((): string | null => {
|
||||
const promptForRescheduleDelayedUntil = useCallback((): string | null => {
|
||||
const value = window.prompt(t('reschedule_prompt'));
|
||||
if (!value) return null;
|
||||
const time = new Date(value).getTime();
|
||||
@@ -518,8 +518,8 @@ export function EmailList({
|
||||
size="sm"
|
||||
className="h-7 px-2"
|
||||
onClick={() => {
|
||||
const sendAt = promptForRescheduleSendAt();
|
||||
if (sendAt) onRescheduleScheduled?.(thread.latestEmail, sendAt);
|
||||
const delayedUntil = promptForRescheduleDelayedUntil();
|
||||
if (delayedUntil) onRescheduleScheduled?.(thread.latestEmail, delayedUntil);
|
||||
}}
|
||||
>
|
||||
<CalendarClock className="w-3.5 h-3.5 mr-1" />
|
||||
@@ -581,8 +581,8 @@ export function EmailList({
|
||||
onCancelScheduled={() => onCancelScheduled?.(contextMenu.data!)}
|
||||
onCancelScheduledForEdit={() => onCancelScheduledForEdit?.(contextMenu.data!)}
|
||||
onRescheduleScheduled={() => {
|
||||
const sendAt = promptForRescheduleSendAt();
|
||||
if (sendAt) onRescheduleScheduled?.(contextMenu.data!, sendAt);
|
||||
const delayedUntil = promptForRescheduleDelayedUntil();
|
||||
if (delayedUntil) onRescheduleScheduled?.(contextMenu.data!, delayedUntil);
|
||||
}}
|
||||
onBatchMarkAsRead={(read) => client && batchMarkAsRead(client, read)}
|
||||
onBatchDelete={() => client && batchDelete(client)}
|
||||
|
||||
@@ -121,7 +121,7 @@ interface EmailViewerProps {
|
||||
onEditDraft?: () => void;
|
||||
onCancelScheduled?: () => void;
|
||||
onCancelScheduledForEdit?: () => void;
|
||||
onRescheduleScheduled?: (sendAt: string) => void;
|
||||
onRescheduleScheduled?: (delayedUntil: string) => void;
|
||||
onCompose?: () => void;
|
||||
currentUserEmail?: string;
|
||||
currentUserName?: string;
|
||||
@@ -883,7 +883,7 @@ export function EmailViewer({
|
||||
const { isTablet, isMobile } = useDeviceDetection();
|
||||
const { tabletListVisible } = useUIStore();
|
||||
const { identities, client, isDemoMode, activeAccountId } = useAuthStore();
|
||||
const promptForRescheduleSendAt = useCallback((): string | null => {
|
||||
const promptForRescheduleDelayedUntil = useCallback((): string | null => {
|
||||
const value = window.prompt(t('reschedule_prompt'));
|
||||
if (!value) return null;
|
||||
const time = new Date(value).getTime();
|
||||
@@ -3147,8 +3147,8 @@ export function EmailViewer({
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
const sendAt = promptForRescheduleSendAt();
|
||||
if (sendAt) onRescheduleScheduled?.(sendAt);
|
||||
const delayedUntil = promptForRescheduleDelayedUntil();
|
||||
if (delayedUntil) onRescheduleScheduled?.(delayedUntil);
|
||||
}}
|
||||
className="hidden sm:flex sm:h-8"
|
||||
title={t('reschedule_send')}
|
||||
@@ -4598,8 +4598,8 @@ export function EmailViewer({
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
const sendAt = promptForRescheduleSendAt();
|
||||
if (sendAt) onRescheduleScheduled?.(sendAt);
|
||||
const delayedUntil = promptForRescheduleDelayedUntil();
|
||||
if (delayedUntil) onRescheduleScheduled?.(delayedUntil);
|
||||
}}
|
||||
>
|
||||
{t('reschedule_send')}
|
||||
|
||||
Reference in New Issue
Block a user