diff --git a/internal_packages/search-bar/lib/main.cjsx b/internal_packages/search-bar/lib/main.cjsx
deleted file mode 100644
index f9b9d2dc4..000000000
--- a/internal_packages/search-bar/lib/main.cjsx
+++ /dev/null
@@ -1,14 +0,0 @@
-path = require 'path'
-{ComponentRegistry, WorkspaceStore, React} = require 'nylas-exports'
-SearchBar = require './search-bar'
-
-module.exports =
- configDefaults:
- showOnRightSide: false
-
- activate: (@state) ->
- ComponentRegistry.register SearchBar,
- location: WorkspaceStore.Location.ThreadList.Toolbar
-
- deactivate: ->
- ComponentRegistry.unregister SearchBar
diff --git a/internal_packages/search-bar/.gitignore b/internal_packages/thread-search/.gitignore
similarity index 100%
rename from internal_packages/search-bar/.gitignore
rename to internal_packages/thread-search/.gitignore
diff --git a/internal_packages/search-bar/README.md b/internal_packages/thread-search/README.md
similarity index 100%
rename from internal_packages/search-bar/README.md
rename to internal_packages/thread-search/README.md
diff --git a/internal_packages/search-bar/keymaps/search-bar.cson b/internal_packages/thread-search/keymaps/search-bar.cson
similarity index 100%
rename from internal_packages/search-bar/keymaps/search-bar.cson
rename to internal_packages/thread-search/keymaps/search-bar.cson
diff --git a/internal_packages/thread-search/lib/main.es6 b/internal_packages/thread-search/lib/main.es6
new file mode 100644
index 000000000..edf7a0bfe
--- /dev/null
+++ b/internal_packages/thread-search/lib/main.es6
@@ -0,0 +1,18 @@
+import {ComponentRegistry, WorkspaceStore} from 'nylas-exports'
+import SearchBar from './search-bar'
+
+export default {
+ configDefaults: {
+ showOnRightSide: false,
+ },
+
+ activate() {
+ ComponentRegistry.register(SearchBar, {
+ location: WorkspaceStore.Location.ThreadList.Toolbar,
+ })
+ },
+
+ deactivate() {
+ ComponentRegistry.unregister(SearchBar)
+ },
+}
diff --git a/internal_packages/search-bar/lib/search-actions.coffee b/internal_packages/thread-search/lib/search-actions.coffee
similarity index 91%
rename from internal_packages/search-bar/lib/search-actions.coffee
rename to internal_packages/thread-search/lib/search-actions.coffee
index d65237c60..dca38ef17 100644
--- a/internal_packages/search-bar/lib/search-actions.coffee
+++ b/internal_packages/thread-search/lib/search-actions.coffee
@@ -4,6 +4,7 @@ SearchActions = Reflux.createActions [
"querySubmitted"
"queryChanged"
"searchBlurred"
+ "searchCompleted"
]
for key, action of SearchActions
diff --git a/internal_packages/search-bar/lib/search-bar.cjsx b/internal_packages/thread-search/lib/search-bar.cjsx
similarity index 81%
rename from internal_packages/search-bar/lib/search-bar.cjsx
rename to internal_packages/thread-search/lib/search-bar.cjsx
index e4c7f7bd8..f9d68c82e 100644
--- a/internal_packages/search-bar/lib/search-bar.cjsx
+++ b/internal_packages/thread-search/lib/search-bar.cjsx
@@ -8,7 +8,7 @@ classNames = require 'classnames'
FocusedPerspectiveStore} = require 'nylas-exports'
{Menu, RetinaImg, KeyCommandsRegion} = require 'nylas-component-kit'
-SearchSuggestionStore = require './search-suggestion-store'
+SearchStore = require './search-store'
SearchActions = require './search-actions'
class SearchBar extends React.Component
@@ -21,7 +21,7 @@ class SearchBar extends React.Component
componentDidMount: =>
@usub = []
- @usub.push SearchSuggestionStore.listen @_onChange
+ @usub.push SearchStore.listen @_onChange
@usub.push WorkspaceStore.listen =>
@setState(focused: false) if @state.focused
@@ -39,6 +39,22 @@ class SearchBar extends React.Component
inputClass = classNames
'empty': @state.query.length is 0
+ loupeImg = if @state.isSearching
+
+ else
+
+
headerComponents = [
-
-
+ onBlur={@_onBlur} />,
+ loupeImg,
+ onClick={@_onClearSearch} />,
]
itemContentFunc = (item) =>
@@ -128,10 +139,12 @@ class SearchBar extends React.Component
_doSearch: =>
SearchActions.querySubmitted(@state.query)
- _onChange: => @setState @_getStateFromStores()
+ _onChange: =>
+ @setState @_getStateFromStores()
_getStateFromStores: =>
- query: SearchSuggestionStore.query()
- suggestions: SearchSuggestionStore.suggestions()
+ query: SearchStore.query()
+ suggestions: SearchStore.suggestions()
+ isSearching: SearchStore.isSearching()
module.exports = SearchBar
diff --git a/internal_packages/thread-search/lib/search-mailbox-perspective.es6 b/internal_packages/thread-search/lib/search-mailbox-perspective.es6
new file mode 100644
index 000000000..6674dc77d
--- /dev/null
+++ b/internal_packages/thread-search/lib/search-mailbox-perspective.es6
@@ -0,0 +1,46 @@
+import _ from 'underscore'
+import {AccountStore, TaskFactory, MailboxPerspective} from 'nylas-exports'
+import SearchQuerySubscription from './search-query-subscription'
+
+
+class SearchMailboxPerspective extends MailboxPerspective {
+
+ constructor(accountIds, searchQuery) {
+ super(accountIds)
+ this.searchQuery = searchQuery
+ this.name = 'Search'
+
+ if (!_.isString(this.searchQuery)) {
+ throw new Error("SearchMailboxPerspective: Expected a `string` search query")
+ }
+ return this
+ }
+
+ emptyMessage() {
+ return "No search results available"
+ }
+
+ isEqual(other) {
+ return super.isEqual(other) && other.searchQuery === this.searchQuery
+ }
+
+ threads() {
+ return new SearchQuerySubscription(this.searchQuery, this.accountIds)
+ }
+
+ canReceiveThreadsFromAccountIds() {
+ return false
+ }
+
+ tasksForRemovingItems(threads) {
+ TaskFactory.tasksForApplyingCategories({
+ threads: threads,
+ categoriesToAdd: (accountId) => {
+ const account = AccountStore.accountForId(accountId)
+ return [account.defaultFinishedCategory()]
+ },
+ })
+ }
+}
+
+export default SearchMailboxPerspective;
diff --git a/src/search-query-subscription.es6 b/internal_packages/thread-search/lib/search-query-subscription.es6
similarity index 86%
rename from src/search-query-subscription.es6
rename to internal_packages/thread-search/lib/search-query-subscription.es6
index e79fee5f6..4cf2c0539 100644
--- a/src/search-query-subscription.es6
+++ b/internal_packages/thread-search/lib/search-query-subscription.es6
@@ -1,18 +1,18 @@
import _ from 'underscore'
-import Actions from './flux/actions'
-import NylasAPI from './flux/nylas-api'
-import Thread from './flux/models/thread'
-import DatabaseStore from './flux/stores/database-store'
-import MutableQuerySubscription from './flux/models/mutable-query-subscription'
-
-let FocusedPerspectiveStore = null
+import {
+ Actions,
+ NylasAPI,
+ Thread,
+ DatabaseStore,
+ FocusedContentStore,
+ MutableQuerySubscription,
+} from 'nylas-exports'
+import SearchActions from './search-actions'
class SearchQuerySubscription extends MutableQuerySubscription {
constructor(searchQuery, accountIds) {
- FocusedPerspectiveStore = require('./flux/stores/focused-content-store')
-
super(null, {asResultSet: true})
this._searchQuery = searchQuery
this._accountIds = accountIds
@@ -21,11 +21,15 @@ class SearchQuerySubscription extends MutableQuerySubscription {
this._connections = []
this._unsubscribers = [
- FocusedPerspectiveStore.listen(::this.onFocusedContentChanged),
+ FocusedContentStore.listen(::this.onFocusedContentChanged),
]
_.defer(() => this.performSearch())
}
+ replaceRange = () => {
+ // TODO
+ }
+
resetData() {
this._searchStartedAt = null
this._resultsReceivedAt = null
@@ -34,10 +38,6 @@ class SearchQuerySubscription extends MutableQuerySubscription {
this._focusedThreadCount = 0
}
- replaceRange = () => {
- // TODO
- }
-
performSearch() {
this._searchStartedAt = Date.now()
@@ -62,10 +62,11 @@ class SearchQuerySubscription extends MutableQuerySubscription {
const accountsSearched = new Set()
let resultIds = []
+ const allAccountsSearched = () => accountsSearched.size === this._accountIds.length
const resultsReturned = () => {
// Don't emit a "result" until we have at least one thread to display.
// Otherwise it will show "No Results Found"
- if (resultIds.length > 0 || accountsSearched.size === this._accountIds.length) {
+ if (resultIds.length > 0 || allAccountsSearched()) {
const currentResults = this._set && this._set.ids().length > 0
if (currentResults) {
const currentResultIds = this._set.ids()
@@ -95,6 +96,9 @@ class SearchQuerySubscription extends MutableQuerySubscription {
onStatusChanged: (conn) => {
if (conn.isClosed()) {
accountsSearched.add(accountId)
+ if (allAccountsSearched()) {
+ SearchActions.searchCompleted()
+ }
resultsReturned()
}
},
@@ -103,7 +107,7 @@ class SearchQuerySubscription extends MutableQuerySubscription {
}
onFocusedContentChanged() {
- const thread = FocusedPerspectiveStore.focused('thread')
+ const thread = FocusedContentStore.focused('thread')
const shouldRecordChange = (
thread &&
(this._lastFocusedThread || {}).id !== thread.id
@@ -163,4 +167,4 @@ class SearchQuerySubscription extends MutableQuerySubscription {
}
}
-module.exports = SearchQuerySubscription
+export default SearchQuerySubscription
diff --git a/internal_packages/search-bar/lib/search-suggestion-store.coffee b/internal_packages/thread-search/lib/search-store.coffee
similarity index 84%
rename from internal_packages/search-bar/lib/search-suggestion-store.coffee
rename to internal_packages/thread-search/lib/search-store.coffee
index b7f347dcb..f620d313c 100644
--- a/internal_packages/search-bar/lib/search-suggestion-store.coffee
+++ b/internal_packages/thread-search/lib/search-store.coffee
@@ -1,32 +1,50 @@
_ = require 'underscore'
NylasStore = require 'nylas-store'
-{Contact,
- Thread,
+{Thread,
+ Contact,
Actions,
- DatabaseStore,
+ ContactStore,
AccountStore,
- FocusedPerspectiveStore,
- MailboxPerspective,
- ContactStore} = require 'nylas-exports'
+ DatabaseStore,
+ FocusedPerspectiveStore} = require 'nylas-exports'
SearchActions = require './search-actions'
+SearchMailboxPerspective = require './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 SearchSuggestionStore extends NylasStore
+class SearchStore extends NylasStore
constructor: ->
@_searchQuery = FocusedPerspectiveStore.current().searchQuery ? ""
@_searchSuggestionsVersion = 1
+ @_isSearching = false
@_clearResults()
@listenTo FocusedPerspectiveStore, @_onPerspectiveChanged
@listenTo SearchActions.querySubmitted, @_onQuerySubmitted
@listenTo SearchActions.queryChanged, @_onQueryChanged
@listenTo SearchActions.searchBlurred, @_onSearchBlurred
+ @listenTo SearchActions.searchCompleted, @_onSearchCompleted
+
+ query: =>
+ @_searchQuery
+
+ queryPopulated: =>
+ @_searchQuery and @_searchQuery.trim().length > 0
+
+ suggestions: =>
+ @_suggestions
+
+ isSearching: =>
+ @_isSearching
+
+ _onSearchCompleted: =>
+ @_isSearching = false
+ @trigger()
_onPerspectiveChanged: =>
@_searchQuery = FocusedPerspectiveStore.current().searchQuery ? ""
@@ -43,11 +61,12 @@ class SearchSuggestionStore extends NylasStore
if @queryPopulated()
Actions.recordUserEvent("Commit Search Query", {})
+ @_isSearching = true
@_perspectiveBeforeSearch ?= current
- next = MailboxPerspective.forSearch(current.accountIds, @_searchQuery.trim())
+ next = new SearchMailboxPerspective(current.accountIds, @_searchQuery.trim())
Actions.focusMailboxPerspective(next)
- else if current.isSearch()
+ else if current instanceof SearchMailboxPerspective
if @_perspectiveBeforeSearch
Actions.focusMailboxPerspective(@_perspectiveBeforeSearch)
@_perspectiveBeforeSearch = null
@@ -127,12 +146,4 @@ class SearchSuggestionStore extends NylasStore
@trigger()
- # Exposed Data
-
- query: => @_searchQuery
-
- queryPopulated: => @_searchQuery and @_searchQuery.trim().length > 0
-
- suggestions: => @_suggestions
-
-module.exports = new SearchSuggestionStore()
+module.exports = new SearchStore()
diff --git a/internal_packages/search-bar/package.json b/internal_packages/thread-search/package.json
similarity index 69%
rename from internal_packages/search-bar/package.json
rename to internal_packages/thread-search/package.json
index 5f3ddf421..06fba07e4 100755
--- a/internal_packages/search-bar/package.json
+++ b/internal_packages/thread-search/package.json
@@ -1,8 +1,8 @@
{
- "name": "search-bar",
+ "name": "thread-search",
"version": "0.1.0",
"main": "./lib/main",
- "description": "Search bar using React",
+ "description": "Search for threads",
"license": "GPL-3.0",
"private": true,
"engines": {
diff --git a/internal_packages/search-bar/spec/search-bar-spec.cjsx b/internal_packages/thread-search/spec/search-bar-spec.cjsx
similarity index 93%
rename from internal_packages/search-bar/spec/search-bar-spec.cjsx
rename to internal_packages/thread-search/spec/search-bar-spec.cjsx
index 1cf10dd3d..0fa9bf002 100644
--- a/internal_packages/search-bar/spec/search-bar-spec.cjsx
+++ b/internal_packages/thread-search/spec/search-bar-spec.cjsx
@@ -4,7 +4,6 @@ ReactTestUtils = require('react-addons-test-utils')
SearchBar = require '../lib/search-bar'
SearchActions = require '../lib/search-actions'
-SearchSuggestionStore = require '../lib/search-suggestion-store'
describe 'SearchBar', ->
beforeEach ->
diff --git a/internal_packages/search-bar/stylesheets/search-bar.less b/internal_packages/thread-search/stylesheets/search-bar.less
similarity index 93%
rename from internal_packages/search-bar/stylesheets/search-bar.less
rename to internal_packages/thread-search/stylesheets/search-bar.less
index a2a505fe9..a631e0e77 100644
--- a/internal_packages/search-bar/stylesheets/search-bar.less
+++ b/internal_packages/thread-search/stylesheets/search-bar.less
@@ -69,6 +69,13 @@
position: absolute;
top: 8px;
left: @padding-base-horizontal;
+
+ &.loading {
+ top: 4px;
+ left: 7px;
+ width: 15px;
+ height: 15px;
+ }
}
&.clear {
diff --git a/spec/mailbox-perspective-spec.es6 b/spec/mailbox-perspective-spec.es6
index 180dd2791..10dddbf1c 100644
--- a/spec/mailbox-perspective-spec.es6
+++ b/spec/mailbox-perspective-spec.es6
@@ -186,13 +186,6 @@ describe('MailboxPerspective', ()=> {
assertMoved('a2').from('label2').to('trash2')
})
- it('moves to default finished perspective if viewing search', ()=> {
- const perspective = MailboxPerspective.forSearch(this.accountIds, '')
- perspective.tasksForRemovingItems(this.threads, Default)
- assertMoved('a1').to('archive')
- assertMoved('a2').to('trash2')
- });
-
it('unstars if viewing starred', ()=> {
spyOn(TaskFactory, 'taskForInvertingStarred').andReturn({some: 'task'})
const perspective = MailboxPerspective.forStarred(this.accountIds)
diff --git a/src/components/empty-list-state.cjsx b/src/components/empty-list-state.cjsx
index e8837d3fe..f9bcd4164 100644
--- a/src/components/empty-list-state.cjsx
+++ b/src/components/empty-list-state.cjsx
@@ -119,14 +119,10 @@ class EmptyListState extends React.Component
ContentComponent = EmptyPerspectiveState
current = FocusedPerspectiveStore.current()
- messageOverride = if current.isSearch()
- "No search results available"
- else
- "Nothing to display"
-
+ messageOverride = current.emptyMessage()
if @state.syncing
messageOverride = "Please wait while we prepare your mailbox."
- else if FocusedPerspectiveStore.current().isInbox()
+ else if current.isInbox()
ContentComponent = EmptyInboxState
classes = classNames
diff --git a/src/flux/stores/category-store.coffee b/src/flux/stores/category-store.coffee
index 922a63602..5c408738b 100644
--- a/src/flux/stores/category-store.coffee
+++ b/src/flux/stores/category-store.coffee
@@ -134,15 +134,21 @@ class CategoryStore extends NylasStore
# account have been loaded into the local cache
#
whenCategoriesReady: (accountOrId) =>
- if not accountOrId
- Promise.reject('whenCategoriesReady: must pass an account or accountId')
+ account = null
+ account = asAccount(accountOrId) if accountOrId
- account = asAccount(accountOrId)
- categoryCollection = account.categoryCollection()
- categoriesReady = => (
- @categories(account).length > 0 and
- NylasSyncStatusStore.isSyncCompleteForAccount(account.id, categoryCollection)
- )
+ isSyncComplete = =>
+ if account
+ categoryCollection = account.categoryCollection()
+ NylasSyncStatusStore.isSyncCompleteForAccount(account.id, categoryCollection)
+ else
+ accounts = AccountStore.accounts()
+ _.every(accounts, (acc) =>
+ NylasSyncStatusStore.isSyncCompleteForAccount(acc.id, acc.categoryCollection())
+ )
+
+ categoriesReady = =>
+ @categories(account).length > 0 and isSyncComplete()
if not categoriesReady()
return new Promise (resolve) =>
diff --git a/src/flux/stores/focused-perspective-store.coffee b/src/flux/stores/focused-perspective-store.coffee
index 0c115ff23..0acd246ba 100644
--- a/src/flux/stores/focused-perspective-store.coffee
+++ b/src/flux/stores/focused-perspective-store.coffee
@@ -7,22 +7,26 @@ Actions = require '../actions'
class FocusedPerspectiveStore extends NylasStore
constructor: ->
- @_current = @_initCurrentPerspective(NylasEnv.savedState.perspective)
+ @_current = MailboxPerspective.forNothing()
- @listenTo CategoryStore, @_onCategoryStoreChanged
- @listenTo Actions.focusMailboxPerspective, @_onFocusPerspective
- @listenTo Actions.focusDefaultMailboxPerspectiveForAccounts, @_onFocusAccounts
+ CategoryStore.whenCategoriesReady().then =>
+ @listenTo CategoryStore, @_onCategoryStoreChanged
+ @listenTo Actions.focusMailboxPerspective, @_onFocusPerspective
+ @listenTo Actions.focusDefaultMailboxPerspectiveForAccounts, @_onFocusAccounts
+
+ perspective = @_initCurrentPerspective(NylasEnv.savedState.perspective)
+ @_setPerspective(perspective)
_initCurrentPerspective: (savedPerspective, accounts = AccountStore.accounts()) =>
if savedPerspective
- current = MailboxPerspective.fromJSON(savedPerspective)
- if current
+ perspective = MailboxPerspective.fromJSON(savedPerspective)
+ if perspective
accountIds = _.pluck(accounts, 'id')
- accountIdsNotPresent = _.difference(current.accountIds, accountIds)
- current = null if accountIdsNotPresent.length > 0
+ accountIdsNotPresent = _.difference(perspective.accountIds, accountIds)
+ perspective = null if accountIdsNotPresent.length > 0
- current ?= @_defaultPerspective()
- return current
+ perspective ?= @_defaultPerspective()
+ return perspective
# Inbound Events
diff --git a/src/mailbox-perspective.coffee b/src/mailbox-perspective.coffee
index a7f880d4a..a51d2e02e 100644
--- a/src/mailbox-perspective.coffee
+++ b/src/mailbox-perspective.coffee
@@ -6,7 +6,6 @@ AccountStore = require './flux/stores/account-store'
CategoryStore = require './flux/stores/category-store'
DatabaseStore = require './flux/stores/database-store'
OutboxStore = require './flux/stores/outbox-store'
-SearchQuerySubscription = require './search-query-subscription'
ThreadCountsStore = require './flux/stores/thread-counts-store'
MutableQuerySubscription = require './flux/models/mutable-query-subscription'
Thread = require './flux/models/thread'
@@ -35,15 +34,13 @@ class MailboxPerspective
new CategoryMailboxPerspective(categories)
@forStandardCategories: (accountsOrIds, names...) ->
+ # TODO this method is broken
categories = CategoryStore.getStandardCategories(accountsOrIds, names...)
@forCategories(categories)
@forStarred: (accountsOrIds) ->
new StarredMailboxPerspective(accountsOrIds)
- @forSearch: (accountsOrIds, query) ->
- new SearchMailboxPerspective(accountsOrIds, query)
-
@forInbox: (accountsOrIds) =>
@forStandardCategories(accountsOrIds, 'inbox')
@@ -52,14 +49,12 @@ class MailboxPerspective
if json.type is CategoryMailboxPerspective.name
categories = JSON.parse(json.serializedCategories, Utils.registeredObjectReviver)
return @forCategories(categories)
- else if json.type is SearchMailboxPerspective.name
- return @forSearch(json.accountIds, json.searchQuery)
else if json.type is StarredMailboxPerspective.name
return @forStarred(json.accountIds)
else if json.type is DraftsMailboxPerspective.name
return @forDrafts(json.accountIds)
else
- return null
+ return @forInbox(json.accountIds)
catch error
NylasEnv.reportError(new Error("Could not restore mailbox perspective: #{error}"))
return null
@@ -92,8 +87,8 @@ class MailboxPerspective
isArchive: =>
false
- isSearch: =>
- @ instanceof SearchMailboxPerspective
+ emptyMessage: =>
+ "Nothing to display"
categories: =>
[]
@@ -151,38 +146,6 @@ class MailboxPerspective
[]
-class SearchMailboxPerspective extends MailboxPerspective
- constructor: (@accountIds, @searchQuery) ->
- super(@accountIds)
- @name = 'Search'
-
- unless _.isString(@searchQuery)
- throw new Error("SearchMailboxPerspective: Expected a `string` search query")
-
- @
-
- toJSON: =>
- json = super
- json.searchQuery = @searchQuery
- json
-
- isEqual: (other) =>
- super(other) and other.searchQuery is @searchQuery
-
- threads: =>
- new SearchQuerySubscription(@searchQuery, @accountIds)
-
- canReceiveThreadsFromAccountIds: =>
- false
-
- tasksForRemovingItems: (threads) =>
- TaskFactory.tasksForApplyingCategories(
- threads: threads,
- categoriesToAdd: (accountId) =>
- account = AccountStore.accountForId(accountId)
- return [account.defaultFinishedCategory()]
- )
-
class DraftsMailboxPerspective extends MailboxPerspective
constructor: (@accountIds) ->
super(@accountIds)
@@ -191,9 +154,6 @@ class DraftsMailboxPerspective extends MailboxPerspective
@drafts = true # The DraftListStore looks for this
@
- fromJSON: =>
- {type: @constructor.name, accountIds: @accountIds}
-
threads: =>
null