mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 08:16:22 +00:00
feat: search mail for selected text from the desktop app's right-click menu
This commit is contained in:
@@ -220,6 +220,42 @@ describe('DesktopHost', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// mail-25: "Search mail for '…'" on selected text in the native menu.
|
||||
describe('Search mail for selected text', () => {
|
||||
const searchFor = (text: string) => `mail.searchText:${JSON.stringify(text)}`;
|
||||
|
||||
it('runs on the mail page', async () => {
|
||||
const shell = fakeBridge();
|
||||
render(<DesktopHost />);
|
||||
const heard: unknown[] = [];
|
||||
const listen = (e: Event) => { e.preventDefault(); heard.push((e as CustomEvent).detail); };
|
||||
window.addEventListener('bulwark:mail-search', listen);
|
||||
try {
|
||||
shell.run(searchFor(' quarterly report \n'));
|
||||
} finally {
|
||||
window.removeEventListener('bulwark:mail-search', listen);
|
||||
}
|
||||
expect(heard).toEqual([{ text: 'quarterly report' }]);
|
||||
expect(push).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('goes to Mail first from another app', async () => {
|
||||
const shell = fakeBridge();
|
||||
render(<DesktopHost />);
|
||||
shell.run(searchFor('report'));
|
||||
expect(push).toHaveBeenCalledWith('/');
|
||||
});
|
||||
|
||||
it('is on offer to the shell when signed in, with its label in the interface language', async () => {
|
||||
const shell = fakeBridge();
|
||||
render(<DesktopHost />);
|
||||
await waitFor(() => expect(shell.setState).toHaveBeenCalled());
|
||||
expect(shell.setState.mock.lastCall![0]).toMatchObject({ 'mail.searchText': true });
|
||||
await waitFor(() => expect(shell.setLanguages).toHaveBeenCalled());
|
||||
expect(shell.setLanguages.mock.lastCall![0].labels.contextSearchMail).toBe('desktop_mail.context_search_mail');
|
||||
});
|
||||
});
|
||||
|
||||
it('hands the shell its menus in the interface language', async () => {
|
||||
const shell = fakeBridge();
|
||||
act(() => useLocaleStore.setState({ locale: 'fr' }));
|
||||
|
||||
@@ -19,6 +19,7 @@ import { useOfflineStore } from "@/lib/offline/state";
|
||||
import { openNewComposerWindow } from "@/lib/popout/open";
|
||||
import { onReturnedComposerElsewhere } from "@/lib/popout/return-to-main";
|
||||
import { isPopoutWindow } from "@/lib/popout/window-role";
|
||||
import { parkMailSearch, requestMailSearch } from "@/lib/mail-search-request";
|
||||
import { onBeforeSignOut, onFullSignOut } from "@/lib/sign-out-cleanup";
|
||||
import { useAuthStore } from "@/stores/auth-store";
|
||||
import { useEmailStore } from "@/stores/email-store";
|
||||
@@ -89,6 +90,22 @@ export function DesktopHost() {
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Search mail for the text selected under a right-click.
|
||||
if (id.startsWith("mail.searchText:")) {
|
||||
try {
|
||||
const text = JSON.parse(id.slice("mail.searchText:".length)) as unknown;
|
||||
if (typeof text === "string" && text.trim()) {
|
||||
const request = { text: text.trim() };
|
||||
if (!requestMailSearch(request)) {
|
||||
parkMailSearch(request);
|
||||
routerRef.current.push("/");
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Only the shell sends this, and it sends JSON.
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!runDesktopCommand(id)) fallback[id]?.();
|
||||
});
|
||||
}, []);
|
||||
@@ -101,6 +118,7 @@ export function DesktopHost() {
|
||||
const popout = isPopoutWindow();
|
||||
setDesktopBaseState({
|
||||
"link.check": true,
|
||||
"mail.searchText": signedIn && !popout,
|
||||
"shortcuts.singleKeys": singleKeys,
|
||||
"policy.download": canDownloadAnywhere(),
|
||||
"file.print": canPrint,
|
||||
|
||||
@@ -139,6 +139,7 @@ export function shellLabels(t: (key: string, values?: Record<string, string>) =>
|
||||
contextCopyAddress: t('desktopUi.context.copy_address'),
|
||||
contextSaveImage: t('desktopUi.context.save_image'),
|
||||
contextCopyImage: t('desktopUi.context.copy_image'),
|
||||
contextSearchMail: t('desktop_mail.context_search_mail', { text: '{text}' }),
|
||||
unsavedTitle: t('desktopUi.closing.unsaved_title'),
|
||||
unsavedDetail: t('desktopUi.closing.unsaved_detail'),
|
||||
closeAnyway: t('desktopUi.closing.close_anyway'),
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "إغلاق"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "البحث في البريد عن «{text}»"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "قفل التطبيق",
|
||||
"section_desc": "يطلب رمز PIN بعد فترة دون استخدام، في كل نافذة وعلامة تبويب. أثناء القفل يبقى البريد على هذا الجهاز مشفرًا ولا يملك التطبيق مفتاحه.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Tanca"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Cerca «{text}» al correu"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Bloqueig de l'app",
|
||||
"section_desc": "Demana un PIN després d'un temps sense ús, a cada finestra i pestanya. Mentre està bloquejada, el correu d'aquest dispositiu continua xifrat i l'app no en té la clau.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Zavřít"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Hledat v poště „{text}“"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Zámek aplikace",
|
||||
"section_desc": "Po čase bez použití žádá o PIN, v každém okně a na každé kartě. Dokud je zamčeno, zůstává pošta na tomto zařízení zašifrovaná a aplikace k ní nemá klíč.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Luk"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Søg efter “{text}” i mail"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Applås",
|
||||
"section_desc": "Beder om en pinkode efter en tid uden brug, i alle vinduer og faner. Mens den er låst, forbliver mail på denne enhed krypteret, og appen har ingen nøgle til den.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Schließen"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "E-Mails nach „{text}“ durchsuchen"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "App-Sperre",
|
||||
"section_desc": "Fragt nach einer Zeit ohne Nutzung in jedem Fenster und Tab nach einer PIN. Solange gesperrt ist, bleiben E-Mails auf diesem Gerät verschlüsselt und die App hat keinen Schlüssel dafür.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Close"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Search mail for \"{text}\""
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "App lock",
|
||||
"section_desc": "Asks for a PIN after a time without use, in every window and tab. While it's locked, mail on this device stays encrypted and the app has no key for it.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Cerrar"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Buscar «{text}» en el correo"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Bloqueo de la app",
|
||||
"section_desc": "Pide un PIN tras un tiempo sin uso, en cada ventana y pestaña. Mientras está bloqueada, el correo de este dispositivo sigue cifrado y la app no tiene la clave.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Sulge"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Otsi kirjadest „{text}”"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Rakenduse lukk",
|
||||
"section_desc": "Küsib pärast kasutamata aega PIN-koodi, igas aknas ja vahekaardil. Lukus olles jääb selle seadme e-post krüpteerituks ja rakendusel pole selle võtit.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "بستن"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "جستجوی «{text}» در نامهها"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "قفل برنامه",
|
||||
"section_desc": "پس از مدتی بیاستفاده، در هر پنجره و زبانه پین میخواهد. تا وقتی قفل است، ایمیلهای این دستگاه رمزگذاریشده میمانند و برنامه کلید آنها را ندارد.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Fermer"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Rechercher « {text} » dans les messages"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Verrouillage de l'app",
|
||||
"section_desc": "Demande un code PIN après un temps sans utilisation, dans chaque fenêtre et onglet. Tant que l'app est verrouillée, les e-mails sur cet appareil restent chiffrés et l'app n'a pas de clé pour les lire.",
|
||||
|
||||
@@ -6811,6 +6811,9 @@
|
||||
"close": "סגירה"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "חיפוש “{text}” בדואר"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "נעילת אפליקציה",
|
||||
"section_desc": "מבקשת קוד PIN אחרי זמן ללא שימוש, בכל חלון וכרטיסייה. בזמן הנעילה הדואר במכשיר הזה נשאר מוצפן ולאפליקציה אין מפתח אליו.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Bezárás"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "„{text}” keresése a levelek között"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Alkalmazászár",
|
||||
"section_desc": "Használat nélküli idő után PIN-kódot kér, minden ablakban és lapon. Amíg zárolva van, az eszközön lévő levelek titkosítva maradnak, és az alkalmazásnak nincs hozzájuk kulcsa.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Chiudi"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Cerca «{text}» nella posta"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Blocco dell'app",
|
||||
"section_desc": "Chiede un PIN dopo un periodo senza utilizzo, in ogni finestra e scheda. Finché è bloccata, la posta su questo dispositivo resta cifrata e l'app non ha la chiave.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "閉じる"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "メールで「{text}」を検索"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "アプリのロック",
|
||||
"section_desc": "一定時間使わないと、すべてのウィンドウとタブで PIN を求めます。ロック中、このデバイスのメールは暗号化されたままで、アプリには鍵がありません。",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "닫기"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "메일에서 “{text}” 검색"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "앱 잠금",
|
||||
"section_desc": "일정 시간 사용하지 않으면 모든 창과 탭에서 PIN을 요청합니다. 잠겨 있는 동안 이 기기의 메일은 암호화된 상태로 유지되며 앱에는 키가 없습니다.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Aizvērt"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Meklēt pastā “{text}”"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Lietotnes bloķēšana",
|
||||
"section_desc": "Pēc laika bez lietošanas prasa PIN kodu katrā logā un cilnē. Kamēr lietotne ir bloķēta, e-pasts šajā ierīcē paliek šifrēts, un lietotnei nav tā atslēgas.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Хаах"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Захидлаас “{text}”-г хайх"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Аппын түгжээ",
|
||||
"section_desc": "Хэсэг хугацаанд ашиглаагүй бол цонх, таб бүрт ПИН код асууна. Түгжээтэй үед энэ төхөөрөмжийн имэйл шифрлэгдсэн хэвээр байх бөгөөд аппад түлхүүр нь байхгүй.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Lukk"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Søk etter «{text}» i e-post"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Applås",
|
||||
"section_desc": "Ber om en PIN-kode etter en tid uten bruk, i alle vinduer og faner. Mens den er låst, forblir e-post på denne enheten kryptert, og appen har ingen nøkkel til den.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Sluit"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Zoek ‘{text}’ in e-mail"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "App-vergrendeling",
|
||||
"section_desc": "Vraagt na een tijd zonder gebruik om een pincode, in elk venster en tabblad. Zolang de app vergrendeld is, blijft de mail op dit apparaat versleuteld en heeft de app er geen sleutel voor.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Zamknij"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Szukaj „{text}” w poczcie"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Blokada aplikacji",
|
||||
"section_desc": "Prosi o PIN po czasie bezczynności, w każdym oknie i na każdej karcie. Gdy aplikacja jest zablokowana, poczta na tym urządzeniu pozostaje zaszyfrowana, a aplikacja nie ma do niej klucza.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Fechar"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Pesquisar «{text}» no correio"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Bloqueio da app",
|
||||
"section_desc": "Pede um PIN após um tempo sem uso, em todas as janelas e separadores. Enquanto está bloqueada, o correio neste dispositivo fica cifrado e a app não tem a chave.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Închidere"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Căutare „{text}” în e-mail"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Blocarea aplicației",
|
||||
"section_desc": "Cere un PIN după un timp fără utilizare, în fiecare fereastră și filă. Cât timp e blocată, e-mailurile de pe acest dispozitiv rămân criptate și aplicația nu are cheia lor.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Закрыть"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Найти «{text}» в почте"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Блокировка приложения",
|
||||
"section_desc": "Запрашивает PIN-код после периода бездействия, в каждом окне и вкладке. Пока приложение заблокировано, почта на этом устройстве остаётся зашифрованной, и у приложения нет ключа к ней.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Zavrieť"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Hľadať „{text}“ v pošte"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Zámok aplikácie",
|
||||
"section_desc": "Po čase bez používania žiada o PIN, v každom okne a na každej karte. Kým je zamknuté, pošta na tomto zariadení zostáva šifrovaná a aplikácia k nej nemá kľúč.",
|
||||
|
||||
@@ -6818,6 +6818,9 @@
|
||||
"close": "ปิด"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "ค้นหา “{text}” ในอีเมล"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "ล็อกแอป",
|
||||
"section_desc": "ขอ PIN หลังจากไม่ได้ใช้งานระยะหนึ่ง ในทุกหน้าต่างและแท็บ ระหว่างล็อก อีเมลบนอุปกรณ์นี้ยังคงเข้ารหัสอยู่และแอปไม่มีกุญแจ",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Kapat"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "E-postalarda “{text}” ara"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Uygulama kilidi",
|
||||
"section_desc": "Bir süre kullanılmadığında her pencerede ve sekmede PIN ister. Kilitliyken bu cihazdaki e-postalar şifreli kalır ve uygulamada anahtarı olmaz.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "Закрити"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Шукати «{text}» у пошті"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Блокування застосунку",
|
||||
"section_desc": "Запитує PIN-код після часу без використання, у кожному вікні й вкладці. Поки застосунок заблоковано, пошта на цьому пристрої залишається зашифрованою, і застосунок не має ключа до неї.",
|
||||
|
||||
@@ -6822,6 +6822,9 @@
|
||||
"close": "Đóng"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "Tìm “{text}” trong thư"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "Khóa ứng dụng",
|
||||
"section_desc": "Yêu cầu mã PIN sau một thời gian không dùng, trong mọi cửa sổ và thẻ. Khi đang khóa, thư trên thiết bị này vẫn được mã hóa và ứng dụng không có khóa của chúng.",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "關閉"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "在郵件中搜尋「{text}」"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "應用程式鎖定",
|
||||
"section_desc": "一段時間未使用後,在每個視窗和分頁中要求輸入 PIN。鎖定期間,此裝置上的郵件保持加密,應用程式沒有金鑰。",
|
||||
|
||||
@@ -6810,6 +6810,9 @@
|
||||
"close": "关闭"
|
||||
}
|
||||
},
|
||||
"desktop_mail": {
|
||||
"context_search_mail": "在邮件中搜索“{text}”"
|
||||
},
|
||||
"appLock": {
|
||||
"section_title": "应用锁",
|
||||
"section_desc": "一段时间未使用后,在每个窗口和标签页中要求输入 PIN。锁定期间,此设备上的邮件保持加密,应用没有密钥。",
|
||||
|
||||
Reference in New Issue
Block a user