fix: ask in the app's own dialog before sending without a delay the account can't do

This commit is contained in:
Linus Rath
2026-10-28 04:48:12 +01:00
parent 5104a1d376
commit 2693ca0447
32 changed files with 66 additions and 3 deletions
@@ -1,8 +1,9 @@
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { render, screen, fireEvent, waitFor, within } from '@testing-library/react';
import { describe, it, expect, vi, afterEach } from 'vitest';
import React from 'react';
import { EmailComposer } from '../email-composer';
import { RecipientsRejectedError, SubmissionQuotaError } from '@/lib/jmap/client';
import { useSettingsStore } from '@/stores/settings-store';
// ─── Heavy component mocks (mirrors composer-reopened-draft-signature.test.tsx) ─
@@ -246,4 +247,23 @@ describe('composer send failures', () => {
expect(message).toBe('nobody@example.net (550 5.1.2 Relay not allowed.)');
expect(screen.getByDisplayValue('Hello')).toBeInTheDocument();
});
// An account without send delay asks in the app's own dialog, not
// window.confirm, before the message goes out at once.
it('asks in a dialog before sending without the delay the account lacks', async () => {
const nativeConfirm = vi.spyOn(window, 'confirm');
useSettingsStore.setState({ sendDelaySeconds: 10 } as never);
try {
const onSend = vi.fn().mockResolvedValue(undefined);
render(<EmailComposer initialData={DRAFT} onSend={onSend} />);
fireEvent.click(screen.getAllByTestId('composer-send')[0]);
const dialog = await screen.findByRole('alertdialog', { name: 'send_delay_unsupported_title' });
expect(nativeConfirm).not.toHaveBeenCalled();
expect(onSend).not.toHaveBeenCalled();
fireEvent.click(within(dialog).getByRole('button', { name: 'send' }));
await waitFor(() => expect(onSend).toHaveBeenCalledTimes(1));
} finally {
useSettingsStore.setState({ sendDelaySeconds: 0 } as never);
}
});
});
+11 -1
View File
@@ -5,6 +5,8 @@ import { useMvComposeWindow } from "@/components/email/mv-compose-context";
import { useFocusTrap } from "@/hooks/use-focus-trap";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
import { Input } from "@/components/ui/input";
import {
X,
@@ -962,6 +964,8 @@ export function EmailComposer({
const [placeholderWarningDelayedUntil, setPlaceholderWarningDelayedUntil] = useState<string | undefined>();
const [placeholderDontAskAgain, setPlaceholderDontAskAgain] = useState(false);
const [showScheduleDialog, setShowScheduleDialog] = useState(false);
// Asks before sending at once when the account can't hold a message back.
const { dialogProps: noDelayDialogProps, confirm: confirmNoDelay } = useConfirmDialog();
const [showSendMenu, setShowSendMenu] = useState(false);
const sendMenuRef = useRef<HTMLDivElement>(null);
const mobileSendMenuRef = useRef<HTMLDivElement>(null);
@@ -2997,7 +3001,11 @@ export function EmailComposer({
if (composerClient?.hasDelayedSend()) {
return new Date(Date.now() + sendDelaySeconds * 1000).toISOString();
}
const confirmed = window.confirm(t('send_delay_unsupported_confirm'));
const confirmed = await confirmNoDelay({
title: t('send_delay_unsupported_title'),
message: t('send_delay_unsupported_confirm'),
confirmText: t('send'),
});
if (!confirmed) {
throw new Error(t('send_delay_unsupported'));
}
@@ -4819,6 +4827,8 @@ export function EmailComposer({
</div>
)}
<ConfirmDialog {...noDelayDialogProps} />
{showPlaceholderWarning && (
<div
className="fixed inset-0 bg-black/50 backdrop-blur-[1px] flex items-center justify-center z-[60] p-4 animate-in fade-in duration-150"
+5 -1
View File
@@ -3914,7 +3914,11 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
let delayedUntil: string | undefined;
if (sendDelaySeconds > 0) {
if (!client.hasDelayedSend()) {
const confirmed = window.confirm(t('email_composer.send_delay_unsupported_confirm'));
const confirmed = await confirmDialog({
title: t('email_composer.send_delay_unsupported_title'),
message: t('email_composer.send_delay_unsupported_confirm'),
confirmText: t('email_composer.send'),
});
if (!confirmed) return;
} else {
delayedUntil = new Date(Date.now() + sendDelaySeconds * 1000).toISOString();
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "تم إنشاء الإرسال المجدول، لكن فشل تنظيف المسودة.",
"send_delay_unsupported": "تأخير الإرسال غير مدعوم لهذا الحساب.",
"send_delay_unsupported_confirm": "هذا الحساب لا يدعم تأخير الإرسال. هل تريد الإرسال فورًا بدلاً من ذلك؟",
"send_delay_unsupported_title": "الإرسال دون تأخير؟",
"recipient_edit_email": "تعديل عنوان البريد الإلكتروني",
"recipient_edit_name": "تعديل الاسم المعروض",
"recipient_email_placeholder": "عنوان البريد الإلكتروني",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "S'ha creat l'enviament programat, però no s'ha pogut netejar l'esborrany.",
"send_delay_unsupported": "Aquest compte no admet el retard d'enviament.",
"send_delay_unsupported_confirm": "Aquest compte no admet el retard d'enviament. Voleu enviar-lo immediatament?",
"send_delay_unsupported_title": "Vols enviar-lo sense retard?",
"recipient_edit_email": "Edita l'adreça electrònica",
"recipient_edit_name": "Edita el nom mostrat",
"recipient_email_placeholder": "Adreça electrònica",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Naplánované odeslání bylo vytvořeno, ale vyčištění konceptu selhalo.",
"send_delay_unsupported": "Prodleva odeslání není pro tento účet podporována.",
"send_delay_unsupported_confirm": "Tento účet nepodporuje prodlevu odeslání. Odeslat ihned?",
"send_delay_unsupported_title": "Odeslat bez zpoždění?",
"recipient_edit_email": "Upravit e-mailovou adresu",
"recipient_edit_name": "Upravit zobrazované jméno",
"recipient_email_placeholder": "E-mailová adresa",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Planlagt afsendelse blev oprettet, men oprydning af kladde mislykkedes.",
"send_delay_unsupported": "Sendeforsinkelse understøttes ikke for denne konto.",
"send_delay_unsupported_confirm": "Denne konto understøtter ikke sendeforsinkelse. Send straks i stedet?",
"send_delay_unsupported_title": "Send uden forsinkelse?",
"recipient_edit_email": "Rediger e-mailadresse",
"recipient_edit_name": "Rediger visningsnavn",
"recipient_email_placeholder": "E-mailadresse",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Geplantes Senden wurde erstellt, aber das Bereinigen des Entwurfs ist fehlgeschlagen.",
"send_delay_unsupported": "Sendeverzögerung wird für dieses Konto nicht unterstützt.",
"send_delay_unsupported_confirm": "Dieses Konto unterstützt keine Sendeverzögerung. Stattdessen sofort senden?",
"send_delay_unsupported_title": "Ohne Verzögerung senden?",
"recipient_edit_email": "E-Mail-Adresse bearbeiten",
"recipient_edit_name": "Anzeigenamen bearbeiten",
"recipient_email_placeholder": "E-Mail-Adresse",
+1
View File
@@ -833,6 +833,7 @@
"schedule_send_cleanup_warning": "Scheduled send was created, but draft cleanup failed.",
"send_delay_unsupported": "Send delay is not supported for this account.",
"send_delay_unsupported_confirm": "This account does not support send delay. Send immediately instead?",
"send_delay_unsupported_title": "Send without a delay?",
"recipient_edit_email": "Edit email address",
"recipient_edit_name": "Edit display name",
"recipient_email_placeholder": "Email address",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Se creó el envío programado, pero falló la limpieza del borrador.",
"send_delay_unsupported": "La demora de envío no es compatible con esta cuenta.",
"send_delay_unsupported_confirm": "Esta cuenta no admite demora de envío. ¿Enviar inmediatamente?",
"send_delay_unsupported_title": "¿Enviar sin retraso?",
"recipient_edit_email": "Editar dirección de correo",
"recipient_edit_name": "Editar nombre para mostrar",
"recipient_email_placeholder": "Dirección de correo",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Ajastatud saatmine loodi, kuid mustandi koristamine ebaõnnestus.",
"send_delay_unsupported": "Saatmise viivitus ei ole selle konto jaoks toetatud.",
"send_delay_unsupported_confirm": "See konto ei toeta saatmise viivitust. Kas saata kohe?",
"send_delay_unsupported_title": "Kas saata viivituseta?",
"recipient_edit_email": "Muuda e-posti aadressi",
"recipient_edit_name": "Muuda kuvatavat nime",
"recipient_email_placeholder": "E-posti aadress",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "ارسال زمان‌بندی ایجاد شد اما پاکسازی پیش‌نویس ناموفق بود.",
"send_delay_unsupported": "تأخیر ارسال پشتیبانی نمی‌شود.",
"send_delay_unsupported_confirm": "این حساب تأخیر ارسال ندارد. بلافاصله ارسال شود؟",
"send_delay_unsupported_title": "بدون تأخیر ارسال شود؟",
"recipient_edit_email": "ویرایش آدرس ایمیل",
"recipient_edit_name": "ویرایش نام نمایشی",
"recipient_email_placeholder": "آدرس ایمیل",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "L’envoi planifié a été créé, mais le nettoyage du brouillon a échoué.",
"send_delay_unsupported": "Le délai d’envoi n’est pas pris en charge pour ce compte.",
"send_delay_unsupported_confirm": "Ce compte ne prend pas en charge le délai d’envoi. Envoyer immédiatement ?",
"send_delay_unsupported_title": "Envoyer sans délai ?",
"recipient_edit_email": "Modifier l'adresse e-mail",
"recipient_edit_name": "Modifier le nom d'affichage",
"recipient_email_placeholder": "Adresse e-mail",
+1
View File
@@ -796,6 +796,7 @@
"schedule_send_cleanup_warning": "תזמון שליחה נוצר, אך ניקוי טיוטה נכשל.",
"send_delay_unsupported": "השהיית שליחה אינה נתמכת עבור חשבון זה.",
"send_delay_unsupported_confirm": "חשבון זה אינו תומך בהשהיית שליחה. שלח מיד במקום זאת?",
"send_delay_unsupported_title": "לשלוח ללא השהיה?",
"recipient_edit_email": "ערוך כתובת דוא״ל",
"recipient_edit_name": "ערוך שם תצוגה",
"recipient_email_placeholder": "כתובת דוא״ל",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Az ütemezett küldés létrejött, de a piszkozat tisztítása sikertelen volt.",
"send_delay_unsupported": "A késleltetett küldés nem támogatott ehhez a fiókhoz.",
"send_delay_unsupported_confirm": "Ez a fiók nem támogatja a késleltetett küldést. Elküldjük azonnal?",
"send_delay_unsupported_title": "Küldés késleltetés nélkül?",
"recipient_edit_email": "E-mail-cím szerkesztése",
"recipient_edit_name": "Megjelenített név szerkesztése",
"recipient_email_placeholder": "E-mail-cím",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "L’invio programmato è stato creato, ma la pulizia della bozza non è riuscita.",
"send_delay_unsupported": "Il ritardo di invio non è supportato per questo account.",
"send_delay_unsupported_confirm": "Questo account non supporta il ritardo di invio. Inviare subito?",
"send_delay_unsupported_title": "Inviare senza ritardo?",
"recipient_edit_email": "Modifica indirizzo email",
"recipient_edit_name": "Modifica nome visualizzato",
"recipient_email_placeholder": "Indirizzo email",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "予約送信は作成されましたが、下書きのクリーンアップに失敗しました。",
"send_delay_unsupported": "このアカウントでは送信遅延はサポートされていません。",
"send_delay_unsupported_confirm": "このアカウントは送信遅延に対応していません。すぐに送信しますか?",
"send_delay_unsupported_title": "遅延なしで送信しますか?",
"recipient_edit_email": "メールアドレスを編集",
"recipient_edit_name": "表示名を編集",
"recipient_email_placeholder": "メールアドレス",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "예약 보내기는 생성되었지만 초안 정리에 실패했습니다.",
"send_delay_unsupported": "이 계정은 보내기 지연을 지원하지 않습니다.",
"send_delay_unsupported_confirm": "이 계정은 보내기 지연을 지원하지 않습니다. 바로 보낼까요?",
"send_delay_unsupported_title": "지연 없이 보낼까요?",
"recipient_edit_email": "이메일 주소 편집",
"recipient_edit_name": "표시 이름 편집",
"recipient_email_placeholder": "이메일 주소",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Ieplānotā sūtīšana tika izveidota, bet melnraksta tīrīšana neizdevās.",
"send_delay_unsupported": "Šim kontam sūtīšanas aizture netiek atbalstīta.",
"send_delay_unsupported_confirm": "Šis konts neatbalsta sūtīšanas aizturi. Sūtīt uzreiz?",
"send_delay_unsupported_title": "Sūtīt bez aizkaves?",
"recipient_edit_email": "Rediģēt e-pasta adresi",
"recipient_edit_name": "Rediģēt parādāmo vārdu",
"recipient_email_placeholder": "E-pasta adrese",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Хуваарьт илгээлт үүсгэсэн боловч ноорог цэвэрлэж чадсангүй.",
"send_delay_unsupported": "Илгээлтийн саатал энэ бүртгэлд дэмжигдээгүй.",
"send_delay_unsupported_confirm": "Энэ бүртгэл нь илгээх саатлыг дэмждэггүй. Оронд нь шууд явуулах уу?",
"send_delay_unsupported_title": "Хүлээлтгүйгээр илгээх үү?",
"recipient_edit_email": "Имэйл хаягийг засах",
"recipient_edit_name": "Дэлгэцийн нэрийг засах",
"recipient_email_placeholder": "Имэйл хаяг",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Sendingen ble planlagt, men oppryddingen av utkastet mislyktes.",
"send_delay_unsupported": "Sendeforsinkelse støttes ikke for denne kontoen.",
"send_delay_unsupported_confirm": "Denne kontoen støtter ikke sendeforsinkelse. Send umiddelbart i stedet?",
"send_delay_unsupported_title": "Sende uten forsinkelse?",
"recipient_edit_email": "Rediger e-postadresse",
"recipient_edit_name": "Rediger visningsnavn",
"recipient_email_placeholder": "E-postadresse",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Gepland verzenden is aangemaakt, maar het opschonen van de conceptversie is mislukt.",
"send_delay_unsupported": "Verzendvertraging wordt niet ondersteund voor dit account.",
"send_delay_unsupported_confirm": "Dit account ondersteunt geen verzendvertraging. Meteen verzenden?",
"send_delay_unsupported_title": "Verzenden zonder vertraging?",
"recipient_edit_email": "E-mailadres bewerken",
"recipient_edit_name": "Weergavenaam bewerken",
"recipient_email_placeholder": "E-mailadres",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Zaplanowana wysyłka została utworzona, ale czyszczenie wersji roboczej nie powiodło się.",
"send_delay_unsupported": "Opóźnienie wysyłki nie jest obsługiwane dla tego konta.",
"send_delay_unsupported_confirm": "To konto nie obsługuje opóźnienia wysyłki. Wysłać natychmiast?",
"send_delay_unsupported_title": "Wysłać bez opóźnienia?",
"recipient_edit_email": "Edytuj adres e-mail",
"recipient_edit_name": "Edytuj wyświetlaną nazwę",
"recipient_email_placeholder": "Adres e-mail",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "O envio agendado foi criado, mas a limpeza do rascunho falhou.",
"send_delay_unsupported": "Atraso de envio não é compatível com esta conta.",
"send_delay_unsupported_confirm": "Esta conta não oferece atraso de envio. Enviar imediatamente?",
"send_delay_unsupported_title": "Enviar sem atraso?",
"recipient_edit_email": "Editar endereço de e-mail",
"recipient_edit_name": "Editar nome de exibição",
"recipient_email_placeholder": "Endereço de e-mail",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Trimiterea programată a fost creată, dar curățarea schițelor a eșuat.",
"send_delay_unsupported": "Întârzierea trimiterii nu este acceptată pentru acest cont.",
"send_delay_unsupported_confirm": "Acest cont nu acceptă amânarea trimiterii. Doriți să trimiteți imediat?",
"send_delay_unsupported_title": "Trimiți fără întârziere?",
"recipient_edit_email": "Editați adresa de e-mail",
"recipient_edit_name": "Editați numele afișat",
"recipient_email_placeholder": "Adresă de e-mail",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Запланированная отправка создана, но очистка черновика не удалась.",
"send_delay_unsupported": "Задержка отправки не поддерживается для этой учетной записи.",
"send_delay_unsupported_confirm": "Эта учетная запись не поддерживает задержку отправки. Отправить сразу?",
"send_delay_unsupported_title": "Отправить без задержки?",
"recipient_edit_email": "Изменить адрес эл. почты",
"recipient_edit_name": "Изменить отображаемое имя",
"recipient_email_placeholder": "Адрес эл. почты",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Naplánované odoslanie bolo vytvorené, ale vyčistenie konceptu zlyhalo.",
"send_delay_unsupported": "Oneskorenie odoslania nie je pre tento účet podporované.",
"send_delay_unsupported_confirm": "Tento účet nepodporuje oneskorenie odoslania. Odoslať okamžite?",
"send_delay_unsupported_title": "Odoslať bez oneskorenia?",
"recipient_edit_email": "Upraviť e-mailovú adresu",
"recipient_edit_name": "Upraviť zobrazené meno",
"recipient_email_placeholder": "E-mailová adresa",
+1
View File
@@ -978,6 +978,7 @@
"schedule_send_cleanup_warning": "สร้างการกำหนดเวลาส่งแล้ว แต่การล้างร่างเดิมไม่สำเร็จ",
"send_delay_unsupported": "บัญชีนี้ไม่รองรับการหน่วงเวลาส่ง",
"send_delay_unsupported_confirm": "บัญชีนี้ไม่รองรับการหน่วงเวลาส่ง ต้องการส่งทันทีหรือไม่?",
"send_delay_unsupported_title": "ส่งโดยไม่หน่วงเวลาไหม",
"recipient_edit_email": "แก้ไขที่อยู่อีเมล",
"recipient_edit_name": "แก้ไขชื่อที่แสดง",
"recipient_email_placeholder": "ที่อยู่อีเมล",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Zamanlanmış gönderim oluşturuldu, ancak taslak temizliği başarısız oldu.",
"send_delay_unsupported": "Bu hesap için gönderim gecikmesi desteklenmiyor.",
"send_delay_unsupported_confirm": "Bu hesap gönderim gecikmesini desteklemiyor. Hemen gönderilsin mi?",
"send_delay_unsupported_title": "Gecikme olmadan gönderilsin mi?",
"recipient_edit_email": "E-posta adresini düzenle",
"recipient_edit_name": "Görünen adı düzenle",
"recipient_email_placeholder": "E-posta adresi",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "Заплановане надсилання створено, але очищення чернетки не вдалося.",
"send_delay_unsupported": "Затримка надсилання не підтримується для цього облікового запису.",
"send_delay_unsupported_confirm": "Цей обліковий запис не підтримує затримку надсилання. Надіслати негайно?",
"send_delay_unsupported_title": "Надіслати без затримки?",
"recipient_edit_email": "Змінити адресу ел. пошти",
"recipient_edit_name": "Змінити відображуване ім'я",
"recipient_email_placeholder": "Адреса ел. пошти",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "已建立排程寄送,但清理草稿失敗。",
"send_delay_unsupported": "此帳號不支援傳送延遲。",
"send_delay_unsupported_confirm": "此帳號不支援傳送延遲。是否立即傳送?",
"send_delay_unsupported_title": "不延遲直接傳送?",
"recipient_edit_email": "編輯電子郵件地址",
"recipient_edit_name": "編輯顯示名稱",
"recipient_email_placeholder": "電子郵件地址",
+1
View File
@@ -831,6 +831,7 @@
"schedule_send_cleanup_warning": "已创建计划发送,但清理草稿失败。",
"send_delay_unsupported": "此账户不支持发送延迟。",
"send_delay_unsupported_confirm": "此账户不支持发送延迟。是否立即发送?",
"send_delay_unsupported_title": "不延迟直接发送?",
"recipient_edit_email": "编辑电子邮件地址",
"recipient_edit_name": "编辑显示名称",
"recipient_email_placeholder": "电子邮件地址",