feat: search for an email address simultaneously in to, cc and bcc #304

This commit is contained in:
rustmailer
2026-06-25 02:36:24 +08:00
parent 8542ba0d28
commit cad447275f
21 changed files with 72 additions and 4 deletions
+4
View File
@@ -43,6 +43,10 @@ pub struct EmailSearchFilter {
pub to: Option<String>, pub to: Option<String>,
pub cc: Option<String>, pub cc: Option<String>,
pub bcc: Option<String>, pub bcc: Option<String>,
/// Matches if the address appears in `to`, `cc`, or `bcc` (OR semantics).
pub any_recipient: Option<String>,
/// Matches if the address appears in `from`, `to`, `cc`, or `bcc` (OR semantics).
pub any_participant: Option<String>,
pub since: Option<i64>, pub since: Option<i64>,
pub before: Option<i64>, pub before: Option<i64>,
/// Lower bound (inclusive) on the IMAP server INTERNALDATE timestamp. /// Lower bound (inclusive) on the IMAP server INTERNALDATE timestamp.
+28
View File
@@ -461,6 +461,34 @@ impl IndexManager {
} }
} }
// any_recipient: OR across to, cc, bcc
if let Some(ref v) = filter.any_recipient {
let mut recipient_queries: Vec<(Occur, Box<dyn Query>)> = Vec::new();
for field in [f.f_to_text, f.f_cc_text, f.f_bcc_text] {
let query_parser = QueryParser::for_index(&self.index, vec![field]);
if let Ok(q) = query_parser.parse_query(v) {
recipient_queries.push((Occur::Should, q));
}
}
if !recipient_queries.is_empty() {
subqueries.push((Occur::Must, Box::new(BooleanQuery::new(recipient_queries))));
}
}
// any_participant: OR across from, to, cc, bcc
if let Some(ref v) = filter.any_participant {
let mut participant_queries: Vec<(Occur, Box<dyn Query>)> = Vec::new();
for field in [f.f_from_text, f.f_to_text, f.f_cc_text, f.f_bcc_text] {
let query_parser = QueryParser::for_index(&self.index, vec![field]);
if let Ok(q) = query_parser.parse_query(v) {
participant_queries.push((Occur::Should, q));
}
}
if !participant_queries.is_empty() {
subqueries.push((Occur::Must, Box::new(BooleanQuery::new(participant_queries))));
}
}
if let Some(has) = filter.has_attachment { if let Some(has) = filter.has_attachment {
let lower: Bound<Term>; let lower: Bound<Term>;
let upper: Bound<Term>; let upper: Bound<Term>;
+4 -4
View File
@@ -30,13 +30,13 @@ import {
CommandGroup, CommandGroup,
CommandInput, CommandInput,
CommandItem, CommandItem,
CommandList,
} from "@/components/ui/command" } from "@/components/ui/command"
import { ScrollArea } from "@/components/ui/scroll-area"
export function MailFilterPopover() { export function MailFilterPopover() {
const { t } = useTranslation() const { t } = useTranslation()
const { filter, setFilter } = useSearchContext() const { filter, setFilter } = useSearchContext()
const fields = ['from', 'to', 'cc', 'bcc'] as const const fields = ['from', 'to', 'cc', 'bcc', 'any_recipient', 'any_participant'] as const
const activeCount = fields.filter(k => !!filter[k]).length const activeCount = fields.filter(k => !!filter[k]).length
@@ -196,7 +196,7 @@ function ContactSelectorField({
value={searchTerm} value={searchTerm}
onValueChange={setSearchTerm} onValueChange={setSearchTerm}
/> />
<CommandList className="max-h-[360px]"> <ScrollArea className="h-[360px]">
{isLoading && ( {isLoading && (
<div className="p-4 text-xs text-center opacity-50">{t('search_contacts.loading')}</div> <div className="p-4 text-xs text-center opacity-50">{t('search_contacts.loading')}</div>
)} )}
@@ -227,7 +227,7 @@ function ContactSelectorField({
</div> </div>
)} )}
</CommandGroup> </CommandGroup>
</CommandList> </ScrollArea>
</Command> </Command>
</PopoverContent> </PopoverContent>
</Popover> </Popover>
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "متقدم", "advanced": "متقدم",
"advancedFilters": "فلاتر متقدمة", "advancedFilters": "فلاتر متقدمة",
"any": "الكل", "any": "الكل",
"any_participant": "أي طرف في المراسلة",
"any_recipient": "أي مستلم",
"attachmentName": "اسم المرفق", "attachmentName": "اسم المرفق",
"attachmentsSize": "المرفقات والحجم", "attachmentsSize": "المرفقات والحجم",
"bcc": "نسخة مخفية", "bcc": "نسخة مخفية",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Avanceret", "advanced": "Avanceret",
"advancedFilters": "Avancerede filtre", "advancedFilters": "Avancerede filtre",
"any": "Alle", "any": "Alle",
"any_participant": "Enhver korrespondent",
"any_recipient": "Enhver modtager",
"attachmentName": "Vedhæftningsfilnavn", "attachmentName": "Vedhæftningsfilnavn",
"attachmentsSize": "Vedhæftninger & Størrelse", "attachmentsSize": "Vedhæftninger & Størrelse",
"bcc": "Blindkopi (BCC)", "bcc": "Blindkopi (BCC)",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Erweitert", "advanced": "Erweitert",
"advancedFilters": "Erweiterte Filter", "advancedFilters": "Erweiterte Filter",
"any": "Beliebig", "any": "Beliebig",
"any_participant": "Beliebiger Beteiligter",
"any_recipient": "Beliebiger Empfänger",
"attachmentName": "Anhangsname", "attachmentName": "Anhangsname",
"attachmentsSize": "Anhänge & Größe", "attachmentsSize": "Anhänge & Größe",
"bcc": "BCC", "bcc": "BCC",
+2
View File
@@ -1002,6 +1002,8 @@
"advanced": "Advanced", "advanced": "Advanced",
"advancedFilters": "Advanced Filters", "advancedFilters": "Advanced Filters",
"any": "Any", "any": "Any",
"any_participant": "Any correspondent",
"any_recipient": "Any recipient",
"attachmentName": "Attachment Name", "attachmentName": "Attachment Name",
"attachmentsSize": "Attachments & Size", "attachmentsSize": "Attachments & Size",
"bcc": "BCC", "bcc": "BCC",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Avanzado", "advanced": "Avanzado",
"advancedFilters": "Filtros avanzados", "advancedFilters": "Filtros avanzados",
"any": "Cualquiera", "any": "Cualquiera",
"any_participant": "Cualquier participante",
"any_recipient": "Cualquier destinatario",
"attachmentName": "Nombre del adjunto", "attachmentName": "Nombre del adjunto",
"attachmentsSize": "Adjuntos y tamaño", "attachmentsSize": "Adjuntos y tamaño",
"bcc": "CCO", "bcc": "CCO",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Tarkennettu", "advanced": "Tarkennettu",
"advancedFilters": "Tarkennetut suodattimet", "advancedFilters": "Tarkennetut suodattimet",
"any": "Kaikki", "any": "Kaikki",
"any_participant": "Kuka tahansa osallistuja",
"any_recipient": "Kuka tahansa vastaanottaja",
"attachmentName": "Liitteen nimi", "attachmentName": "Liitteen nimi",
"attachmentsSize": "Liitteet ja koko", "attachmentsSize": "Liitteet ja koko",
"bcc": "Piilokopio", "bcc": "Piilokopio",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Avancé", "advanced": "Avancé",
"advancedFilters": "Filtres Avancés", "advancedFilters": "Filtres Avancés",
"any": "Toutes", "any": "Toutes",
"any_participant": "N'importe quel correspondant",
"any_recipient": "N'importe quel destinataire",
"attachmentName": "Nom de la Pièce Jointe", "attachmentName": "Nom de la Pièce Jointe",
"attachmentsSize": "Pièces Jointes et Taille", "attachmentsSize": "Pièces Jointes et Taille",
"bcc": "Cci", "bcc": "Cci",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Avanzato", "advanced": "Avanzato",
"advancedFilters": "Filtri Avanzati", "advancedFilters": "Filtri Avanzati",
"any": "Qualsiasi", "any": "Qualsiasi",
"any_participant": "Qualsiasi partecipante",
"any_recipient": "Qualsiasi destinatario",
"attachmentName": "Nome Allegato", "attachmentName": "Nome Allegato",
"attachmentsSize": "Allegati e Dimensione", "attachmentsSize": "Allegati e Dimensione",
"bcc": "BCC", "bcc": "BCC",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "高度な設定", "advanced": "高度な設定",
"advancedFilters": "高度なフィルター", "advancedFilters": "高度なフィルター",
"any": "指定なし", "any": "指定なし",
"any_participant": "すべての関係者",
"any_recipient": "すべての受信者",
"attachmentName": "添付ファイル名", "attachmentName": "添付ファイル名",
"attachmentsSize": "添付ファイルとサイズ", "attachmentsSize": "添付ファイルとサイズ",
"bcc": "BCC(ブラインド写し)", "bcc": "BCC(ブラインド写し)",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "고급", "advanced": "고급",
"advancedFilters": "고급 필터", "advancedFilters": "고급 필터",
"any": "전체", "any": "전체",
"any_participant": "모든 관련자",
"any_recipient": "모든 수신자",
"attachmentName": "첨부 파일 이름", "attachmentName": "첨부 파일 이름",
"attachmentsSize": "첨부 파일 및 크기", "attachmentsSize": "첨부 파일 및 크기",
"bcc": "숨은 참조", "bcc": "숨은 참조",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Geavanceerd", "advanced": "Geavanceerd",
"advancedFilters": "Geavanceerde Filters", "advancedFilters": "Geavanceerde Filters",
"any": "Alle", "any": "Alle",
"any_participant": "Elke correspondent",
"any_recipient": "Elke ontvanger",
"attachmentName": "Naam Bijlage", "attachmentName": "Naam Bijlage",
"attachmentsSize": "Bijlagen & Grootte", "attachmentsSize": "Bijlagen & Grootte",
"bcc": "BCC", "bcc": "BCC",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Avansert", "advanced": "Avansert",
"advancedFilters": "Avanserte filtre", "advancedFilters": "Avanserte filtre",
"any": "Alle", "any": "Alle",
"any_participant": "Enhver korrespondent",
"any_recipient": "Enhver mottaker",
"attachmentName": "Vedleggsnavn", "attachmentName": "Vedleggsnavn",
"attachmentsSize": "Vedlegg og størrelse", "attachmentsSize": "Vedlegg og størrelse",
"bcc": "Blindkopi (BCC)", "bcc": "Blindkopi (BCC)",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Zaawansowane", "advanced": "Zaawansowane",
"advancedFilters": "Zaawansowane filtry", "advancedFilters": "Zaawansowane filtry",
"any": "Dowolny", "any": "Dowolny",
"any_participant": "Dowolny uczestnik",
"any_recipient": "Dowolny odbiorca",
"attachmentName": "Nazwa załącznika", "attachmentName": "Nazwa załącznika",
"attachmentsSize": "Załączniki & Rozmiar", "attachmentsSize": "Załączniki & Rozmiar",
"bcc": "UDW", "bcc": "UDW",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Avançado", "advanced": "Avançado",
"advancedFilters": "Filtros Avançados", "advancedFilters": "Filtros Avançados",
"any": "Qualquer", "any": "Qualquer",
"any_participant": "Qualquer correspondente",
"any_recipient": "Qualquer destinatario",
"attachmentName": "Nome do Anexo", "attachmentName": "Nome do Anexo",
"attachmentsSize": "Anexos e Tamanho", "attachmentsSize": "Anexos e Tamanho",
"bcc": "BCC", "bcc": "BCC",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Дополнительно", "advanced": "Дополнительно",
"advancedFilters": "Расширенные фильтры", "advancedFilters": "Расширенные фильтры",
"any": "Любой", "any": "Любой",
"any_participant": "Любой участник переписки",
"any_recipient": "Любой получатель",
"attachmentName": "Имя вложения", "attachmentName": "Имя вложения",
"attachmentsSize": "Вложения и Размер", "attachmentsSize": "Вложения и Размер",
"bcc": "Скрытая копия", "bcc": "Скрытая копия",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "Avancerat", "advanced": "Avancerat",
"advancedFilters": "Avancerade filter", "advancedFilters": "Avancerade filter",
"any": "Alla", "any": "Alla",
"any_participant": "Valfri korrespondent",
"any_recipient": "Valfri mottagare",
"attachmentName": "Filnamn på bilaga", "attachmentName": "Filnamn på bilaga",
"attachmentsSize": "Bilagor & storlek", "attachmentsSize": "Bilagor & storlek",
"bcc": "Hemlig kopia (BCC)", "bcc": "Hemlig kopia (BCC)",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "進階", "advanced": "進階",
"advancedFilters": "進階篩選", "advancedFilters": "進階篩選",
"any": "不限", "any": "不限",
"any_participant": "任意關聯人",
"any_recipient": "任意收件人",
"attachmentName": "附件名稱", "attachmentName": "附件名稱",
"attachmentsSize": "附件與大小", "attachmentsSize": "附件與大小",
"bcc": "密件副本 (BCC)", "bcc": "密件副本 (BCC)",
+2
View File
@@ -1001,6 +1001,8 @@
"advanced": "高级", "advanced": "高级",
"advancedFilters": "高级筛选", "advancedFilters": "高级筛选",
"any": "不限", "any": "不限",
"any_participant": "任意关联人",
"any_recipient": "任意收件人",
"attachmentName": "附件名称", "attachmentName": "附件名称",
"attachmentsSize": "附件和大小", "attachmentsSize": "附件和大小",
"bcc": "密送", "bcc": "密送",