fixes from review

This commit is contained in:
Lucas Gaitzsch
2026-05-22 13:13:13 +02:00
parent 159706a683
commit 5c1d59f38a
4 changed files with 26 additions and 13 deletions
+9 -7
View File
@@ -406,6 +406,7 @@ export function EmailComposer({
const [smimePassphraseError, setSmimePassphraseError] = useState('');
const [showAttachmentWarning, setShowAttachmentWarning] = useState(false);
const [attachmentWarningKeyword, setAttachmentWarningKeyword] = useState('');
const [attachmentWarningDelayedUntil, setAttachmentWarningDelayedUntil] = useState<string | undefined>();
const [showScheduleDialog, setShowScheduleDialog] = useState(false);
const [scheduleValue, setScheduleValue] = useState('');
const [scheduleError, setScheduleError] = useState('');
@@ -1227,8 +1228,8 @@ export function EmailComposer({
const time = new Date(value).getTime();
if (!Number.isFinite(time)) return t('schedule_send_invalid');
if (time <= Date.now()) return t('schedule_send_future');
if (client) {
const maxDelayedSend = client.getMaxDelayedSend();
if (composerClient) {
const maxDelayedSend = composerClient.getMaxDelayedSend();
if (maxDelayedSend > 0 && time > Date.now() + maxDelayedSend * 1000) {
return t('schedule_send_too_late');
}
@@ -1239,7 +1240,7 @@ export function EmailComposer({
const resolveDelayedUntil = async (requestedDelayedUntil?: string): Promise<string | undefined> => {
if (requestedDelayedUntil) return requestedDelayedUntil;
if (sendDelaySeconds === 0) return undefined;
if (client?.hasDelayedSend()) {
if (composerClient?.hasDelayedSend()) {
return new Date(Date.now() + sendDelaySeconds * 1000).toISOString();
}
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()));
if (matched) {
setAttachmentWarningKeyword(matched);
setAttachmentWarningDelayedUntil(delayedUntil);
setShowAttachmentWarning(true);
return;
}
@@ -1654,7 +1656,7 @@ export function EmailComposer({
};
const handleScheduleSend = () => {
if (!client?.hasDelayedSend()) {
if (!composerClient?.hasDelayedSend()) {
setScheduleError(t('schedule_send_unsupported'));
return;
}
@@ -1742,7 +1744,7 @@ export function EmailComposer({
if (isScheduleShortcut) {
e.preventDefault();
if (!e.repeat && client?.hasDelayedSend()) openScheduleDialog();
if (!e.repeat && composerClient?.hasDelayedSend()) openScheduleDialog();
}
};
@@ -2225,7 +2227,7 @@ export function EmailComposer({
>
{t('discard')}
</button>
{client?.hasDelayedSend() ? (
{composerClient?.hasDelayedSend() ? (
<div ref={sendMenuRef} className="relative hidden md:inline-flex">
<Button
onClick={() => handleSend()}
@@ -2412,7 +2414,7 @@ export function EmailComposer({
<Button variant="outline" onClick={() => setShowAttachmentWarning(false)}>
{t('forgot_attachment.back')}
</Button>
<Button onClick={() => { setShowAttachmentWarning(false); handleSend(true); }}>
<Button onClick={() => { setShowAttachmentWarning(false); handleSend(true, attachmentWarningDelayedUntil); setAttachmentWarningDelayedUntil(undefined); }}>
{t('forgot_attachment.send_anyway')}
</Button>
</div>
+3 -3
View File
@@ -471,7 +471,7 @@ export function EmailList({
onToggleExpand={() => handleToggleThreadExpansion(thread.threadId)}
onEmailSelect={(email) => onEmailSelect?.(email)}
onEmailDoubleClick={onEmailDoubleClick ? (email) => onEmailDoubleClick(email) : undefined}
onContextMenu={isScheduledView ? undefined : openContextMenu}
onContextMenu={openContextMenu}
onOpenConversation={onOpenConversation}
onToggleStar={onToggleStar ? (email) => onToggleStar(email) : undefined}
onMarkAsRead={onMarkAsRead ? (email, read) => onMarkAsRead(email, read) : undefined}
@@ -535,8 +535,8 @@ export function EmailList({
onMarkAsSpam={() => onMarkAsSpam?.(contextMenu.data!)}
onUndoSpam={() => onUndoSpam?.(contextMenu.data!)}
onEditDraft={() => onEditDraft?.(contextMenu.data!)}
onCancelScheduled={isScheduledView ? undefined : () => onCancelScheduled?.(contextMenu.data!)}
onCancelScheduledForEdit={isScheduledView ? undefined : () => onCancelScheduledForEdit?.(contextMenu.data!)}
onCancelScheduled={() => onCancelScheduled?.(contextMenu.data!)}
onCancelScheduledForEdit={() => onCancelScheduledForEdit?.(contextMenu.data!)}
onRescheduleScheduled={undefined}
onBatchMarkAsRead={(read) => client && batchMarkAsRead(client, read)}
onBatchDelete={() => client && batchDelete(client)}
+6 -1
View File
@@ -39,7 +39,7 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
const handleSend = useCallback(async (sendData: Parameters<NonNullable<React.ComponentProps<typeof EmailComposer>['onSend']>>[0]) => {
if (!client) return;
try {
await sendEmail(
const result = await sendEmail(
client,
sendData.to,
sendData.subject,
@@ -58,6 +58,11 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
sendData.envelopeMailFrom,
);
if (result.scheduled) {
closeTab(tabIdRef.current);
return;
}
// Mark the original message as $answered / $forwarded so the standard
// viewer and list reflect the action (same behaviour as inline compose).
if (data.sourceEmailId && (data.mode === 'reply' || data.mode === 'replyAll')) {
+8 -2
View File
@@ -2606,7 +2606,13 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
cancelUndoSend: async (client, pending) => {
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 draftsMailbox = mailboxes.find(mb => mb.role === 'drafts');
const sentMailbox = mailboxes.find(mb => mb.role === 'sent');
@@ -2616,7 +2622,7 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
}
await get().refreshScheduledMetadata(client);
set({ pendingUndoSend: null });
return pending.emailId ? client.getEmail(pending.emailId) : null;
return pending.emailId && !pending.isSmime ? client.getEmail(pending.emailId) : null;
},
loadMockData: () => {