mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-30 16:16:19 +00:00
fix: say when a rescheduled message goes out now after Reschedule
This commit is contained in:
@@ -42,7 +42,8 @@ import { findDraftIdentityIdForLogin, listRowSource, resolveDraftSource, type Dr
|
||||
import { useRemoveFromFolder } from "@/hooks/use-remove-from-folder";
|
||||
import { groupSearchScopeFolders, SEARCH_SCOPE_ALL_FOLDERS } from "@/lib/search-scope-folders";
|
||||
import { toast } from "@/stores/toast-store";
|
||||
import { cancelFailureToast, partlyRefusedWarning, rescheduleFailureToast } from "@/lib/send-failure-message";
|
||||
import { cancelFailureToast, partlyRefusedWarning, rescheduleFailureToast, rescheduledToast } from "@/lib/send-failure-message";
|
||||
import { formatScheduledSendAt } from "@/lib/scheduled-send-label";
|
||||
import { formatRejectedRecipients, RescheduleRestoredError } from "@/lib/jmap/client";
|
||||
import { subscribePushStateChanges } from "@/lib/push-events";
|
||||
import { createMailPushListener } from "@/lib/mail-push-listener";
|
||||
@@ -90,7 +91,7 @@ import { isFilterEmpty, activeFilterCount, DEFAULT_SEARCH_FILTERS } from "@/lib/
|
||||
import { SearchBox, type ContactSearchField } from "@/components/search/search-box";
|
||||
import { FilterPanelHost } from "@/components/search/filter-panel-host";
|
||||
import type { ContactSuggestion } from "@/lib/search-suggestions";
|
||||
import type { Attachment } from "@/lib/jmap/types";
|
||||
import type { Attachment, SendEmailResult } from "@/lib/jmap/types";
|
||||
import { peekListAttachments, requestListAttachments, type ListAttachmentSource, type LoadListAttachments } from "@/lib/list-attachments";
|
||||
import { useSearchHistoryStore } from "@/stores/search-history-store";
|
||||
import { WelcomeBanner } from "@/components/ui/welcome-banner";
|
||||
@@ -2431,8 +2432,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
if (!client || !email.emailSubmissionId || !email.scheduledIdentityId) return;
|
||||
try {
|
||||
const result = await rescheduleScheduledEmail(client, email.emailSubmissionId, email.id, email.scheduledIdentityId, delayedUntil, email.scheduledAccountId);
|
||||
const refused = partlyRefusedWarning(result, tComposer);
|
||||
if (refused) toast.warning(refused);
|
||||
announceRescheduled(result, delayedUntil);
|
||||
} catch (error) {
|
||||
console.error('Failed to reschedule the scheduled send:', error);
|
||||
const { title, message } = rescheduleFailureToast(error, tViewer, tComposer, scheduledTime(email.scheduledSendAt));
|
||||
@@ -2443,6 +2443,12 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
const scheduledTime = (sendAt: string | undefined) =>
|
||||
sendAt ? formatDateTime(sendAt, useSettingsStore.getState().timeFormat) : undefined;
|
||||
|
||||
// R9: a reschedule says when the message goes out now.
|
||||
const announceRescheduled = (result: SendEmailResult, delayedUntil: string) => {
|
||||
const { kind, title, message } = rescheduledToast(result, tViewer, tComposer, formatScheduledSendAt(result.sendAt || delayedUntil, useSettingsStore.getState().timeFormat));
|
||||
toast[kind](title, message ? { message } : undefined);
|
||||
};
|
||||
|
||||
const toastCancelFailure = (error: unknown, cancelFailedTitle: string) => {
|
||||
const { title, message } = cancelFailureToast(error, tViewer, cancelFailedTitle);
|
||||
toast.error(title, message ? { message } : undefined);
|
||||
@@ -5652,8 +5658,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
onRescheduleScheduled={async (delayedUntil) => {
|
||||
if (client && selectedEmail?.emailSubmissionId && selectedEmail.scheduledIdentityId) {
|
||||
const result = await rescheduleScheduledEmail(client, selectedEmail.emailSubmissionId, selectedEmail.id, selectedEmail.scheduledIdentityId, delayedUntil, selectedEmail.scheduledAccountId);
|
||||
const refused = partlyRefusedWarning(result, tComposer);
|
||||
if (refused) toast.warning(refused);
|
||||
announceRescheduled(result, delayedUntil);
|
||||
}
|
||||
}}
|
||||
onCompose={() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { NoLongerScheduledError, RecipientsRejectedError, RequestTimeoutError, ScheduleTooLateError, SubmissionQuotaError, formatRejectedRecipients } from '../jmap/client';
|
||||
import { SendCancelledCleanupError, cancelFailureToast, partlyRefusedWarning, rescheduleFailureToast, sendFailureMessage } from '../send-failure-message';
|
||||
import { SendCancelledCleanupError, cancelFailureToast, partlyRefusedWarning, rescheduleFailureToast, rescheduledToast, sendFailureMessage } from '../send-failure-message';
|
||||
|
||||
// Keys of a namespace, returned with their values so the test sees which
|
||||
// message was chosen and what went into it.
|
||||
@@ -107,6 +107,21 @@ describe('partlyRefusedWarning', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// A reschedule that went through said nothing at all.
|
||||
describe('rescheduledToast', () => {
|
||||
it('says when the message goes out now', () => {
|
||||
expect(rescheduledToast({}, tViewer, t, 'Thu, Oct 29, 08:00'))
|
||||
.toEqual({ kind: 'success', title: 'email_viewer.rescheduled_for {"date":"Thu, Oct 29, 08:00"}' });
|
||||
});
|
||||
|
||||
it('warns about the recipients the server refused', () => {
|
||||
const toast = rescheduledToast({ rejectedRecipients: [{ email: 'nobody@example.net', smtpReply: '550 5.1.1 No such user' }] }, tViewer, t, 'Mon, Nov 2, 08:00');
|
||||
expect(toast.kind).toBe('warning');
|
||||
expect(toast.title).toBe('email_viewer.rescheduled_for {"date":"Mon, Nov 2, 08:00"}');
|
||||
expect(toast.message).toContain('email_composer.recipients_partly_refused');
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancelFailureToast', () => {
|
||||
it('reports a failed cancel with its reason: the mail may still go out', () => {
|
||||
expect(cancelFailureToast(new Error('The requested message is no longer in the queue.'), tViewer, 'Undo send failed'))
|
||||
|
||||
@@ -93,6 +93,21 @@ export function partlyRefusedWarning(result: { rejectedRecipients?: RejectedReci
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The toast after a reschedule the server took (R9): when the message goes
|
||||
* out now (`sendAt`, formatted), and whom the server refused, if anyone.
|
||||
*/
|
||||
export function rescheduledToast(
|
||||
result: { rejectedRecipients?: RejectedRecipient[] },
|
||||
tViewer: Translate,
|
||||
tComposer: Translate,
|
||||
sendAt: string,
|
||||
): { kind: 'success' | 'warning'; title: string; message?: string } {
|
||||
const title = tViewer('rescheduled_for', { date: sendAt });
|
||||
const refused = partlyRefusedWarning(result, tComposer);
|
||||
return refused ? { kind: 'warning', title, message: refused } : { kind: 'success', title };
|
||||
}
|
||||
|
||||
/**
|
||||
* The toast for a failed "Undo send" or "Cancel and edit". A failed cancel
|
||||
* may still let the mail go, so it must never read as done: it gets
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "أُرسلت هذه الرسالة بالفعل.",
|
||||
"no_longer_scheduled": "لم تعد هذه الرسالة مجدولة.",
|
||||
"reschedule_recipients_refused": "لم يتغيّر شيء: رفض الخادم {recipients}.",
|
||||
"rescheduled_for": "أُعيدت الجدولة إلى {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "جهة الاتصال",
|
||||
"close": "إغلاق الشريط الجانبي",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Aquest missatge ja s'ha enviat.",
|
||||
"no_longer_scheduled": "Aquest missatge ja no està programat.",
|
||||
"reschedule_recipients_refused": "No s'ha canviat res: el servidor ha rebutjat {recipients}.",
|
||||
"rescheduled_for": "S'ha reprogramat per a {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Contacte",
|
||||
"close": "Tanca la barra lateral",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Tato zpráva již byla odeslána.",
|
||||
"no_longer_scheduled": "Tato zpráva už není naplánována.",
|
||||
"reschedule_recipients_refused": "Nic se nezměnilo: server odmítl {recipients}.",
|
||||
"rescheduled_for": "Přeplánováno na {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Kontakt",
|
||||
"close": "Zavřít postranní panel",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Denne besked er allerede sendt.",
|
||||
"no_longer_scheduled": "Denne besked er ikke længere planlagt.",
|
||||
"reschedule_recipients_refused": "Intet blev ændret: serveren afviste {recipients}.",
|
||||
"rescheduled_for": "Omplanlagt til {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Kontakt",
|
||||
"close": "Luk sidepanel",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Diese Nachricht wurde bereits gesendet.",
|
||||
"no_longer_scheduled": "Diese Nachricht ist nicht mehr geplant.",
|
||||
"reschedule_recipients_refused": "Nichts wurde geändert: Der Server hat {recipients} abgelehnt.",
|
||||
"rescheduled_for": "Neu geplant für {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Kontakt",
|
||||
"close": "Seitenleiste schließen",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "This message was already sent.",
|
||||
"no_longer_scheduled": "This message is no longer scheduled.",
|
||||
"reschedule_recipients_refused": "Nothing was changed: the server refused {recipients}.",
|
||||
"rescheduled_for": "Rescheduled for {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Contact",
|
||||
"close": "Close sidebar",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Este mensaje ya se envió.",
|
||||
"no_longer_scheduled": "Este mensaje ya no está programado.",
|
||||
"reschedule_recipients_refused": "No se cambió nada: el servidor rechazó {recipients}.",
|
||||
"rescheduled_for": "Reprogramado para {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Contacto",
|
||||
"close": "Cerrar panel",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "See kiri on juba saadetud.",
|
||||
"no_longer_scheduled": "See kiri ei ole enam ajastatud.",
|
||||
"reschedule_recipients_refused": "Midagi ei muudetud: server lükkas tagasi {recipients}.",
|
||||
"rescheduled_for": "Ajastatud ümber: {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Kontakt",
|
||||
"close": "Sulge külgriba",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "این پیام قبلاً ارسال شده است.",
|
||||
"no_longer_scheduled": "این پیام دیگر زمانبندی نشده است.",
|
||||
"reschedule_recipients_refused": "چیزی تغییر نکرد: سرور {recipients} را رد کرد.",
|
||||
"rescheduled_for": "زمانبندی دوباره برای {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "مخاطب",
|
||||
"close": "بستن نوار کناری",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Ce message a déjà été envoyé.",
|
||||
"no_longer_scheduled": "Ce message n’est plus planifié.",
|
||||
"reschedule_recipients_refused": "Rien n’a été modifié : le serveur a refusé {recipients}.",
|
||||
"rescheduled_for": "Reprogrammé pour le {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Contact",
|
||||
"close": "Fermer le panneau",
|
||||
|
||||
@@ -637,6 +637,7 @@
|
||||
"already_sent": "ההודעה הזו כבר נשלחה.",
|
||||
"no_longer_scheduled": "ההודעה הזו כבר אינה מתוזמנת.",
|
||||
"reschedule_recipients_refused": "דבר לא השתנה: השרת דחה את {recipients}.",
|
||||
"rescheduled_for": "התזמון שונה ל-{date}",
|
||||
"contact_sidebar": {
|
||||
"title": "איש קשר",
|
||||
"close": "סגור סרגל צד",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Ez az üzenet már el lett küldve.",
|
||||
"no_longer_scheduled": "Ez az üzenet már nincs ütemezve.",
|
||||
"reschedule_recipients_refused": "Semmi sem változott: a kiszolgáló elutasította a következőt: {recipients}.",
|
||||
"rescheduled_for": "Átütemezve: {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Névjegy",
|
||||
"close": "Oldalsáv bezárása",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Questo messaggio è già stato inviato.",
|
||||
"no_longer_scheduled": "Questo messaggio non è più programmato.",
|
||||
"reschedule_recipients_refused": "Non è stato cambiato nulla: il server ha rifiutato {recipients}.",
|
||||
"rescheduled_for": "Riprogrammato per {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Contatto",
|
||||
"close": "Chiudi barra laterale",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "このメッセージはすでに送信されています。",
|
||||
"no_longer_scheduled": "このメッセージはもう送信予定ではありません。",
|
||||
"reschedule_recipients_refused": "何も変更されていません: サーバーが {recipients} を拒否しました。",
|
||||
"rescheduled_for": "{date} に再スケジュールしました",
|
||||
"contact_sidebar": {
|
||||
"title": "連絡先",
|
||||
"close": "サイドバーを閉じる",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "이 메시지는 이미 보냈습니다.",
|
||||
"no_longer_scheduled": "이 메시지는 더 이상 예약되어 있지 않습니다.",
|
||||
"reschedule_recipients_refused": "변경된 내용이 없습니다: 서버가 {recipients}을(를) 거부했습니다.",
|
||||
"rescheduled_for": "{date}(으)로 예약을 변경했습니다",
|
||||
"contact_sidebar": {
|
||||
"title": "연락처",
|
||||
"close": "사이드바 닫기",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Šis ziņojums jau ir nosūtīts.",
|
||||
"no_longer_scheduled": "Šis ziņojums vairs nav ieplānots.",
|
||||
"reschedule_recipients_refused": "Nekas netika mainīts: serveris noraidīja {recipients}.",
|
||||
"rescheduled_for": "Pārplānots uz {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Kontaktpersona",
|
||||
"close": "Aizvērt sānjoslu",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Энэ мессеж аль хэдийн илгээгдсэн.",
|
||||
"no_longer_scheduled": "Энэ мессеж товлогдоогүй болсон.",
|
||||
"reschedule_recipients_refused": "Юу ч өөрчлөгдсөнгүй: сервер дараах хүлээн авагчдыг татгалзлаа: {recipients}.",
|
||||
"rescheduled_for": "{date}-нд дахин товлогдлоо",
|
||||
"contact_sidebar": {
|
||||
"title": "Холбоо барих",
|
||||
"close": "Хажуугийн самбарыг хаах",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Denne meldingen er allerede sendt.",
|
||||
"no_longer_scheduled": "Denne meldingen er ikke lenger planlagt.",
|
||||
"reschedule_recipients_refused": "Ingenting ble endret: serveren avviste {recipients}.",
|
||||
"rescheduled_for": "Planlagt på nytt til {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Kontakt",
|
||||
"close": "Lukk sidefeltet",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Dit bericht is al verzonden.",
|
||||
"no_longer_scheduled": "Dit bericht staat niet meer gepland.",
|
||||
"reschedule_recipients_refused": "Er is niets gewijzigd: de server heeft {recipients} geweigerd.",
|
||||
"rescheduled_for": "Opnieuw gepland voor {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Contact",
|
||||
"close": "Zijbalk sluiten",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Ta wiadomość została już wysłana.",
|
||||
"no_longer_scheduled": "Ta wiadomość nie jest już zaplanowana.",
|
||||
"reschedule_recipients_refused": "Nic nie zostało zmienione: serwer odrzucił {recipients}.",
|
||||
"rescheduled_for": "Przełożono na {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Kontakt",
|
||||
"close": "Zamknij panel boczny",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Esta mensagem já foi enviada.",
|
||||
"no_longer_scheduled": "Esta mensagem não está mais agendada.",
|
||||
"reschedule_recipients_refused": "Nada foi alterado: o servidor recusou {recipients}.",
|
||||
"rescheduled_for": "Reagendado para {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Contato",
|
||||
"close": "Fechar barra lateral",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Acest mesaj a fost deja trimis.",
|
||||
"no_longer_scheduled": "Acest mesaj nu mai este programat.",
|
||||
"reschedule_recipients_refused": "Nu s-a schimbat nimic: serverul a respins {recipients}.",
|
||||
"rescheduled_for": "Reprogramat pentru {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Contact",
|
||||
"close": "Închide bara laterală",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Это письмо уже отправлено.",
|
||||
"no_longer_scheduled": "Это письмо больше не запланировано.",
|
||||
"reschedule_recipients_refused": "Ничего не изменилось: сервер отклонил {recipients}.",
|
||||
"rescheduled_for": "Перенесено на {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Контакт",
|
||||
"close": "Закрыть боковую панель",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Táto správa už bola odoslaná.",
|
||||
"no_longer_scheduled": "Táto správa už nie je naplánovaná.",
|
||||
"reschedule_recipients_refused": "Nič sa nezmenilo: server odmietol {recipients}.",
|
||||
"rescheduled_for": "Preplánované na {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Kontakt",
|
||||
"close": "Zavrieť postranný panel",
|
||||
|
||||
@@ -1432,6 +1432,7 @@
|
||||
"already_sent": "ข้อความนี้ถูกส่งออกไปแล้ว",
|
||||
"no_longer_scheduled": "ข้อความนี้ไม่มีกำหนดเวลาส่งแล้ว",
|
||||
"reschedule_recipients_refused": "ไม่มีการเปลี่ยนแปลง: เซิร์ฟเวอร์ปฏิเสธผู้รับ {recipients}",
|
||||
"rescheduled_for": "กำหนดเวลาใหม่เป็น {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "ข้อมูลผู้ติดต่อ",
|
||||
"close": "ปิด",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Bu ileti zaten gönderildi.",
|
||||
"no_longer_scheduled": "Bu ileti artık zamanlanmış değil.",
|
||||
"reschedule_recipients_refused": "Hiçbir şey değiştirilmedi: sunucu şu alıcıları reddetti: {recipients}.",
|
||||
"rescheduled_for": "{date} için yeniden zamanlandı",
|
||||
"contact_sidebar": {
|
||||
"title": "Kişi",
|
||||
"close": "Kenar çubuğunu kapat",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "Цей лист уже надіслано.",
|
||||
"no_longer_scheduled": "Цей лист більше не заплановано.",
|
||||
"reschedule_recipients_refused": "Нічого не змінено: сервер відхилив {recipients}.",
|
||||
"rescheduled_for": "Перенесено на {date}",
|
||||
"contact_sidebar": {
|
||||
"title": "Контакт",
|
||||
"close": "Закрити бічну панель",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "此郵件已寄出。",
|
||||
"no_longer_scheduled": "此郵件已不在排定傳送中。",
|
||||
"reschedule_recipients_refused": "未作任何變更:伺服器拒絕了 {recipients}。",
|
||||
"rescheduled_for": "已改為在 {date} 傳送",
|
||||
"contact_sidebar": {
|
||||
"title": "聯絡人",
|
||||
"close": "關閉側邊欄",
|
||||
|
||||
@@ -672,6 +672,7 @@
|
||||
"already_sent": "此邮件已发送。",
|
||||
"no_longer_scheduled": "此邮件已不在定时发送中。",
|
||||
"reschedule_recipients_refused": "未作任何更改:服务器拒绝了 {recipients}。",
|
||||
"rescheduled_for": "已改为在 {date} 发送",
|
||||
"contact_sidebar": {
|
||||
"title": "联系人",
|
||||
"close": "关闭侧边栏",
|
||||
|
||||
Reference in New Issue
Block a user