diff --git a/src/app/components/header/AdvancedSearchDropdown.tsx b/src/app/components/header/AdvancedSearchDropdown.tsx index 5b2fabaa..54b208c1 100644 --- a/src/app/components/header/AdvancedSearchDropdown.tsx +++ b/src/app/components/header/AdvancedSearchDropdown.tsx @@ -36,7 +36,7 @@ import { extractSearchParameters, keywordToString } from '../../helpers/mailboxU import './AdvancedSearchDropdown.scss'; -interface SearchModel { +export interface SearchModel { keyword: string; labelID: string; from: Recipient[]; @@ -54,7 +54,7 @@ interface LabelInfo { group: string; } -const UNDEFINED = undefined; +export const UNDEFINED = undefined; const AUTO_WILDCARD = undefined; const ALL_ADDRESSES = 'all'; const NO_ATTACHMENTS = 0; @@ -75,7 +75,7 @@ const getRecipients = (value = '') => .split(',') .filter(validateEmailAddress) .map((Address) => ({ Address, Name: '' })); -const formatRecipients = (recipients: Recipient[] = []) => recipients.map(({ Address }) => Address).join(','); +export const formatRecipients = (recipients: Recipient[] = []) => recipients.map(({ Address }) => Address).join(','); const folderReducer = (acc: LabelInfo[], folder: FolderWithSubFolders, level = 0) => { acc.push({ @@ -347,4 +347,57 @@ const AdvancedSearchDropdown = ({ labelID, keyword: fullInput = '', location, hi ); }; +export const parseSearch = (search) => { + const model = { + ...DEFAULT_MODEL, + keyword: search || '', + }; + + const keywords = []; + search.split(' ').forEach((part) => { + const parsed = part.match(/^([^:]*):(.*)$/); + if (parsed && parsed[1]) { + switch (parsed[1]) { + case 'from': + case 'to': + model[parsed[1]] = getRecipients(parsed[2]); + return; + case 'has': + if (parsed[2]) { + switch (parsed[2]) { + case 'attachment': + model.attachments = WITH_ATTACHMENTS; + return; + default: + } + } + break; + default: + } + } + keywords.push(part); + }); + model.keyword = keywords.join(' '); + return model; +}; + +export const generateSearch = (location) => { + const { keyword = '', from = undefined, to = undefined, attachments = undefined } = extractSearchParameters( + location + ); + + const search = [keyword]; + if (from) { + search.push(`from:${from}`); + } + if (to) { + search.push(`to:${to}`); + } + if (attachments) { + search.push('has:attachment'); + } + + return search.join(' '); +}; + export default AdvancedSearchDropdown; diff --git a/src/app/components/header/MailHeader.tsx b/src/app/components/header/MailHeader.tsx index 902cacd1..b62a31ca 100644 --- a/src/app/components/header/MailHeader.tsx +++ b/src/app/components/header/MailHeader.tsx @@ -12,8 +12,8 @@ import { } from 'react-components'; import { MAILBOX_LABEL_IDS, APPS } from 'proton-shared/lib/constants'; -import AdvancedSearchDropdown from './AdvancedSearchDropdown'; -import { extractSearchParameters, setParamsInUrl } from '../../helpers/mailboxUrl'; +import AdvancedSearchDropdown, { generateSearch } from './AdvancedSearchDropdown'; +import { setParamsInUrl } from '../../helpers/mailboxUrl'; import { Breakpoints } from '../../models/utils'; import { getLabelName } from '../../helpers/labels'; import { OnCompose } from '../../hooks/useCompose'; @@ -42,7 +42,7 @@ const MailHeader = ({ onSearch, onCompose, }: Props) => { - const { keyword = '' } = extractSearchParameters(location); + const keyword = generateSearch(location); const [value, updateValue] = useState(keyword); const [oldLabelID, setOldLabelID] = useState(MAILBOX_LABEL_IDS.INBOX); const [labels = []] = useLabels(); diff --git a/src/app/components/layout/PrivateLayout.tsx b/src/app/components/layout/PrivateLayout.tsx index cb33ec10..a8dad640 100644 --- a/src/app/components/layout/PrivateLayout.tsx +++ b/src/app/components/layout/PrivateLayout.tsx @@ -1,14 +1,15 @@ import React, { useState, useEffect, ReactNode, useCallback } from 'react'; import { PrivateAppContainer } from 'react-components'; import { MAILBOX_LABEL_IDS } from 'proton-shared/lib/constants'; +import { changeSearchParams } from 'proton-shared/lib/helpers/url'; import { Location, History } from 'history'; -import MailHeader from '../header/MailHeader'; import MailSidebar from '../sidebar/MailSidebar'; +import MailHeader from '../header/MailHeader'; import { getHumanLabelID } from '../../helpers/labels'; -import { setKeywordInUrl } from '../../helpers/mailboxUrl'; import { Breakpoints } from '../../models/utils'; import { OnCompose } from '../../hooks/useCompose'; +import { parseSearch, formatRecipients, UNDEFINED } from '../header/AdvancedSearchDropdown'; interface Props { children: ReactNode; @@ -33,8 +34,20 @@ const PrivateLayout = ({ }: Props) => { const [expanded, setExpand] = useState(false); - const handleSearch = useCallback((keyword = '', labelID = MAILBOX_LABEL_IDS.ALL_MAIL as string) => { - history.push(setKeywordInUrl({ ...location, pathname: `/${getHumanLabelID(labelID)}` }, keyword)); + const handleSearch = useCallback((search = '', labelID = MAILBOX_LABEL_IDS.ALL_MAIL as string) => { + const model = parseSearch(search); + model.labelID = labelID; + + const { keyword, from, to, attachments } = model; + + history.push( + changeSearchParams(`/${getHumanLabelID(model.labelID)}`, location.search, { + keyword: keyword || UNDEFINED, + from: from.length ? formatRecipients(from) : UNDEFINED, + to: to.length ? formatRecipients(to) : UNDEFINED, + attachments: typeof attachments === 'number' ? String(attachments) : UNDEFINED, + }) + ); }, []); const handleToggleExpand = useCallback(() => setExpand((expanded) => !expanded), []);