feat(scheduled): Send now button on scheduled/delayed messages

Adds a 'Send now' action to the scheduled-send view (both the toolbar and the
inline banner) so a queued message — whether explicitly scheduled or held by the
undo-send delay — can be sent immediately instead of only cancel/reschedule/edit.
Reuses the existing reschedule path (reschedules the submission to now), so no
new JMAP plumbing. Toolbar 'Cancel send' demoted to ghost so 'Send now' is the
single primary action. i18n added across locales.
This commit is contained in:
Shuki Vaknin
2026-06-24 16:46:18 +02:00
committed by Linus Rath
parent f1b9f4ba50
commit baf094d026
20 changed files with 50 additions and 20 deletions
+12 -1
View File
@@ -3515,7 +3515,17 @@ export function EmailViewer({
)} )}
{isScheduled && canCancelScheduled && ( {isScheduled && canCancelScheduled && (
<> <>
<Button variant="default" size="sm" onClick={onCancelScheduled} className="sm:flex sm:h-8" title={t('cancel_scheduled_send')}> <Button
variant="default"
size="sm"
onClick={() => onRescheduleScheduled?.(new Date(Date.now() + 1000).toISOString())}
className="sm:flex sm:h-8"
title={t('send_now')}
>
<Send className="w-4 h-4" />
{showToolbarLabels && <span className="hidden sm:inline text-sm">{t('send_now')}</span>}
</Button>
<Button variant="ghost" size="sm" onClick={onCancelScheduled} className="sm:flex sm:h-8" title={t('cancel_scheduled_send')}>
<X className="w-4 h-4" /> <X className="w-4 h-4" />
<span className="hidden sm:inline text-sm">{t('cancel_scheduled_send')}</span> <span className="hidden sm:inline text-sm">{t('cancel_scheduled_send')}</span>
</Button> </Button>
@@ -5029,6 +5039,7 @@ export function EmailViewer({
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{canCancelScheduled && ( {canCancelScheduled && (
<> <>
<Button size="sm" variant="default" onClick={() => onRescheduleScheduled?.(new Date(Date.now() + 1000).toISOString())}>{t('send_now')}</Button>
<Button size="sm" variant="outline" onClick={onCancelScheduled}>{t('cancel_scheduled_send')}</Button> <Button size="sm" variant="outline" onClick={onCancelScheduled}>{t('cancel_scheduled_send')}</Button>
<Button <Button
size="sm" size="sm"
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Přidat do kontaktů", "add_to_contacts": "Přidat do kontaktů",
"copied": "Zkopírováno!", "copied": "Zkopírováno!",
"copy_failed": "Kopírování se nezdařilo" "copy_failed": "Kopírování se nezdařilo"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Vyžádáno potvrzení o přečtení (kliknutím vypnete)", "read_receipt_on": "Vyžádáno potvrzení o přečtení (kliknutím vypnete)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Tilføj til kontakter", "add_to_contacts": "Tilføj til kontakter",
"copied": "Kopieret!", "copied": "Kopieret!",
"copy_failed": "Kunne ikke kopiere" "copy_failed": "Kunne ikke kopiere"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Læsekvittering anmodet (klik for at deaktivere)", "read_receipt_on": "Læsekvittering anmodet (klik for at deaktivere)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Zu Kontakten hinzufügen", "add_to_contacts": "Zu Kontakten hinzufügen",
"copied": "Kopiert!", "copied": "Kopiert!",
"copy_failed": "Kopieren fehlgeschlagen" "copy_failed": "Kopieren fehlgeschlagen"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Lesebestätigung angefordert (klicken zum Deaktivieren)", "read_receipt_on": "Lesebestätigung angefordert (klicken zum Deaktivieren)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Add to contacts", "add_to_contacts": "Add to contacts",
"copied": "Copied!", "copied": "Copied!",
"copy_failed": "Failed to copy" "copy_failed": "Failed to copy"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Read receipt requested (click to disable)", "read_receipt_on": "Read receipt requested (click to disable)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Añadir a contactos", "add_to_contacts": "Añadir a contactos",
"copied": "¡Copiado!", "copied": "¡Copiado!",
"copy_failed": "Error al copiar" "copy_failed": "Error al copiar"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Confirmación de lectura solicitada (haz clic para desactivar)", "read_receipt_on": "Confirmación de lectura solicitada (haz clic para desactivar)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Ajouter aux contacts", "add_to_contacts": "Ajouter aux contacts",
"copied": "Copié !", "copied": "Copié !",
"copy_failed": "Échec de la copie" "copy_failed": "Échec de la copie"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Accusé de lecture demandé (cliquez pour désactiver)", "read_receipt_on": "Accusé de lecture demandé (cliquez pour désactiver)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Hozzáadás a névjegyekhez", "add_to_contacts": "Hozzáadás a névjegyekhez",
"copied": "Másolva!", "copied": "Másolva!",
"copy_failed": "A másolás nem sikerült" "copy_failed": "A másolás nem sikerült"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Olvasási visszaigazolás kérve (kattints a letiltáshoz)", "read_receipt_on": "Olvasási visszaigazolás kérve (kattints a letiltáshoz)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Aggiungi ai contatti", "add_to_contacts": "Aggiungi ai contatti",
"copied": "Copiato!", "copied": "Copiato!",
"copy_failed": "Copia non riuscita" "copy_failed": "Copia non riuscita"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Conferma di lettura richiesta (clicca per disattivare)", "read_receipt_on": "Conferma di lettura richiesta (clicca per disattivare)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "連絡先に追加", "add_to_contacts": "連絡先に追加",
"copied": "コピーしました!", "copied": "コピーしました!",
"copy_failed": "コピーに失敗しました" "copy_failed": "コピーに失敗しました"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "開封確認を要求中(クリックで無効化)", "read_receipt_on": "開封確認を要求中(クリックで無効化)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "연락처에 추가", "add_to_contacts": "연락처에 추가",
"copied": "복사됨!", "copied": "복사됨!",
"copy_failed": "복사하지 못했습니다" "copy_failed": "복사하지 못했습니다"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "읽음 확인 요청됨 (클릭하여 해제)", "read_receipt_on": "읽음 확인 요청됨 (클릭하여 해제)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Pievienot kontaktpersonām", "add_to_contacts": "Pievienot kontaktpersonām",
"copied": "Nokopēts!", "copied": "Nokopēts!",
"copy_failed": "Neizdevās nokopēt" "copy_failed": "Neizdevās nokopēt"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Pieprasīts lasīšanas apstiprinājums (noklikšķiniet, lai atspējotu)", "read_receipt_on": "Pieprasīts lasīšanas apstiprinājums (noklikšķiniet, lai atspējotu)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Aan contacten toevoegen", "add_to_contacts": "Aan contacten toevoegen",
"copied": "Gekopieerd!", "copied": "Gekopieerd!",
"copy_failed": "Kopiëren mislukt" "copy_failed": "Kopiëren mislukt"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Leesbevestiging aangevraagd (klik om uit te schakelen)", "read_receipt_on": "Leesbevestiging aangevraagd (klik om uit te schakelen)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Dodaj do kontaktów", "add_to_contacts": "Dodaj do kontaktów",
"copied": "Skopiowano!", "copied": "Skopiowano!",
"copy_failed": "Nie udało się skopiować" "copy_failed": "Nie udało się skopiować"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Zażądano potwierdzenia przeczytania (kliknij, aby wyłączyć)", "read_receipt_on": "Zażądano potwierdzenia przeczytania (kliknij, aby wyłączyć)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Adicionar aos contatos", "add_to_contacts": "Adicionar aos contatos",
"copied": "Copiado!", "copied": "Copiado!",
"copy_failed": "Falha ao copiar" "copy_failed": "Falha ao copiar"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Confirmação de leitura solicitada (clique para desativar)", "read_receipt_on": "Confirmação de leitura solicitada (clique para desativar)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Adaugă la contacte", "add_to_contacts": "Adaugă la contacte",
"copied": "Copiat!", "copied": "Copiat!",
"copy_failed": "Copierea a eșuat" "copy_failed": "Copierea a eșuat"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Solicitare confirmare de citire (faceți clic pentru a dezactiva)", "read_receipt_on": "Solicitare confirmare de citire (faceți clic pentru a dezactiva)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Добавить в контакты", "add_to_contacts": "Добавить в контакты",
"copied": "Скопировано!", "copied": "Скопировано!",
"copy_failed": "Не удалось скопировать" "copy_failed": "Не удалось скопировать"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Запрошено уведомление о прочтении (нажмите, чтобы отключить)", "read_receipt_on": "Запрошено уведомление о прочтении (нажмите, чтобы отключить)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Kişilere ekle", "add_to_contacts": "Kişilere ekle",
"copied": "Kopyalandı!", "copied": "Kopyalandı!",
"copy_failed": "Kopyalanamadı" "copy_failed": "Kopyalanamadı"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Okundu bilgisi istendi (devre dışı bırakmak için tıklayın)", "read_receipt_on": "Okundu bilgisi istendi (devre dışı bırakmak için tıklayın)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "Додати до контактів", "add_to_contacts": "Додати до контактів",
"copied": "Скопійовано!", "copied": "Скопійовано!",
"copy_failed": "Не вдалося скопіювати" "copy_failed": "Не вдалося скопіювати"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "Запитано сповіщення про прочитання (натисніть, щоб вимкнути)", "read_receipt_on": "Запитано сповіщення про прочитання (натисніть, щоб вимкнути)",
+2 -1
View File
@@ -565,7 +565,8 @@
"add_to_contacts": "添加到联系人", "add_to_contacts": "添加到联系人",
"copied": "已复制!", "copied": "已复制!",
"copy_failed": "复制失败" "copy_failed": "复制失败"
} },
"send_now": "Send now"
}, },
"email_composer": { "email_composer": {
"read_receipt_on": "已请求已读回执(点击以关闭)", "read_receipt_on": "已请求已读回执(点击以关闭)",