mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-11 02:30:21 +08:00
2d2621d2f3
Summary: Previously we were using the raw visitors that were confined to the flux attributes directory. We're going to add more search query backends, so this is mostly just moving things to a new, more general place. Test Plan: Run locally, verify parser specs still work, verify in-app search still works. Reviewers: spang, evan, juan Reviewed By: juan Differential Revision: https://phab.nylas.com/D4053
220 lines
6.3 KiB
JavaScript
220 lines
6.3 KiB
JavaScript
import NylasStore from 'nylas-store';
|
|
import {
|
|
Thread,
|
|
Actions,
|
|
ContactStore,
|
|
AccountStore,
|
|
DatabaseStore,
|
|
ComponentRegistry,
|
|
FocusedPerspectiveStore,
|
|
SearchQueryParser,
|
|
} from 'nylas-exports';
|
|
|
|
import SearchActions from './search-actions';
|
|
import SearchMailboxPerspective from './search-mailbox-perspective';
|
|
|
|
// Stores should closely match the needs of a particular part of the front end.
|
|
// For example, we might create a "MessageStore" that observes this store
|
|
// for changes in selectedThread, "DatabaseStore" for changes to the underlying database,
|
|
// and vends up the array used for that view.
|
|
|
|
class SearchStore extends NylasStore {
|
|
constructor() {
|
|
super();
|
|
|
|
this._searchQuery = FocusedPerspectiveStore.current().searchQuery || "";
|
|
this._searchSuggestionsVersion = 1;
|
|
this._isSearching = false;
|
|
this._extensionData = []
|
|
this._clearResults();
|
|
|
|
this.listenTo(FocusedPerspectiveStore, this._onPerspectiveChanged);
|
|
this.listenTo(SearchActions.querySubmitted, this._onQuerySubmitted);
|
|
this.listenTo(SearchActions.queryChanged, this._onQueryChanged);
|
|
this.listenTo(SearchActions.searchBlurred, this._onSearchBlurred);
|
|
this.listenTo(SearchActions.searchCompleted, this._onSearchCompleted);
|
|
}
|
|
|
|
query() {
|
|
return this._searchQuery;
|
|
}
|
|
|
|
queryPopulated() {
|
|
return this._searchQuery && this._searchQuery.trim().length > 0;
|
|
}
|
|
|
|
suggestions() {
|
|
return this._suggestions;
|
|
}
|
|
|
|
isSearching() {
|
|
return this._isSearching;
|
|
}
|
|
|
|
_onSearchCompleted = () => {
|
|
this._isSearching = false;
|
|
this.trigger();
|
|
}
|
|
|
|
_onPerspectiveChanged = () => {
|
|
this._searchQuery = FocusedPerspectiveStore.current().searchQuery || "";
|
|
this.trigger();
|
|
}
|
|
|
|
_onQueryChanged = (query) => {
|
|
this._searchQuery = query;
|
|
if (this._searchQuery.length <= 1) {
|
|
this.trigger()
|
|
return
|
|
}
|
|
this._compileResults();
|
|
setTimeout(() => this._rebuildResults(), 0);
|
|
}
|
|
|
|
_onQuerySubmitted = (query) => {
|
|
this._searchQuery = query;
|
|
const current = FocusedPerspectiveStore.current();
|
|
|
|
if (this.queryPopulated()) {
|
|
this._isSearching = true;
|
|
if (this._perspectiveBeforeSearch == null) {
|
|
this._perspectiveBeforeSearch = current;
|
|
}
|
|
const next = new SearchMailboxPerspective(current.accountIds, this._searchQuery.trim());
|
|
Actions.focusMailboxPerspective(next);
|
|
} else if (current instanceof SearchMailboxPerspective) {
|
|
if (this._perspectiveBeforeSearch) {
|
|
Actions.focusMailboxPerspective(this._perspectiveBeforeSearch);
|
|
this._perspectiveBeforeSearch = null;
|
|
} else {
|
|
Actions.focusDefaultMailboxPerspectiveForAccounts(AccountStore.accounts());
|
|
}
|
|
}
|
|
|
|
this._clearResults();
|
|
}
|
|
|
|
_onSearchBlurred = () => {
|
|
this._clearResults();
|
|
}
|
|
|
|
_clearResults() {
|
|
this._searchSuggestionsVersion = 1;
|
|
this._threadResults = [];
|
|
this._contactResults = [];
|
|
this._suggestions = [];
|
|
this.trigger();
|
|
}
|
|
|
|
_rebuildResults() {
|
|
if (!this.queryPopulated()) {
|
|
this._clearResults();
|
|
return;
|
|
}
|
|
this._searchSuggestionsVersion += 1;
|
|
const searchExtensions = ComponentRegistry.findComponentsMatching({
|
|
role: "SearchBarResults",
|
|
})
|
|
|
|
Promise.map(searchExtensions, (ext) => {
|
|
return Promise.props({
|
|
label: ext.searchLabel(),
|
|
suggestions: ext.fetchSearchSuggestions(this._searchQuery),
|
|
})
|
|
}).then((extensionData = []) => {
|
|
this._extensionData = extensionData;
|
|
this._compileResults();
|
|
})
|
|
|
|
this._fetchThreadResults();
|
|
this._fetchContactResults();
|
|
}
|
|
|
|
_fetchContactResults() {
|
|
const version = this._searchSuggestionsVersion;
|
|
ContactStore.searchContacts(this._searchQuery, {limit: 10}).then(contacts => {
|
|
if (version !== this._searchSuggestionsVersion) {
|
|
return;
|
|
}
|
|
this._contactResults = contacts;
|
|
this._compileResults();
|
|
});
|
|
}
|
|
|
|
_fetchThreadResults() {
|
|
if (this._fetchingThreadResultsVersion) { return; }
|
|
this._fetchingThreadResultsVersion = this._searchSuggestionsVersion;
|
|
|
|
const {accountIds} = FocusedPerspectiveStore.current();
|
|
let dbQuery = DatabaseStore.findAll(Thread).distinct()
|
|
if (Array.isArray(accountIds) && accountIds.length === 1) {
|
|
dbQuery = dbQuery.where({accountId: accountIds[0]})
|
|
}
|
|
|
|
try {
|
|
const parsedQuery = SearchQueryParser.parse(this._searchQuery);
|
|
// console.info('Successfully parsed and codegened search query', parsedQuery);
|
|
dbQuery = dbQuery.structuredSearch(parsedQuery);
|
|
} catch (e) {
|
|
// console.info('Failed to parse local search query, falling back to generic query', e);
|
|
dbQuery = dbQuery.search(this._searchQuery);
|
|
}
|
|
dbQuery = dbQuery
|
|
.order(Thread.attributes.lastMessageReceivedTimestamp.descending())
|
|
|
|
// console.info(dbQuery.sql());
|
|
|
|
dbQuery.background().then(results => {
|
|
// We've fetched the latest thread results - display them!
|
|
if (this._searchSuggestionsVersion === this._fetchingThreadResultsVersion) {
|
|
this._fetchingThreadResultsVersion = null;
|
|
this._threadResults = results;
|
|
this._compileResults();
|
|
// We're behind and need to re-run the search for the latest results
|
|
} else if (this._searchSuggestionsVersion > this._fetchingThreadResultsVersion) {
|
|
this._fetchingThreadResultsVersion = null;
|
|
this._fetchThreadResults();
|
|
} else {
|
|
this._fetchingThreadResultsVersion = null;
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
_compileResults() {
|
|
this._suggestions = [];
|
|
|
|
this._suggestions.push({
|
|
label: `Message Contains: ${this._searchQuery}`,
|
|
value: this._searchQuery,
|
|
});
|
|
|
|
if (this._threadResults.length) {
|
|
this._suggestions.push({divider: 'Threads'});
|
|
for (const thread of this._threadResults) {
|
|
this._suggestions.push({thread});
|
|
}
|
|
}
|
|
|
|
if (this._contactResults.length) {
|
|
this._suggestions.push({divider: 'People'});
|
|
for (const contact of this._contactResults) {
|
|
this._suggestions.push({
|
|
contact: contact,
|
|
value: contact.email,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (this._extensionData.length) {
|
|
for (const {label, suggestions} of this._extensionData) {
|
|
this._suggestions.push({divider: label});
|
|
this._suggestions = this._suggestions.concat(suggestions)
|
|
}
|
|
}
|
|
|
|
this.trigger();
|
|
}
|
|
}
|
|
|
|
export default new SearchStore();
|