mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-11 10:12:00 +08:00
fix(category-store): Fix issue with observables in CategoryStore
- Removes use of observables from category store and keeps a big cache of categories per account - Upates Category Observables with new helper observables - Updates CategoryPicker and AccountSidebarStore to use observables - Misc fixes
This commit is contained in:
parent
2beeccdecd
commit
d265bf1248
16 changed files with 139 additions and 66 deletions
|
@ -2,7 +2,6 @@ NylasStore = require 'nylas-store'
|
|||
_ = require 'underscore'
|
||||
{DatabaseStore,
|
||||
AccountStore,
|
||||
CategoryStore,
|
||||
ThreadCountsStore,
|
||||
WorkspaceStore,
|
||||
Actions,
|
||||
|
@ -16,10 +15,14 @@ _ = require 'underscore'
|
|||
CategoryHelpers,
|
||||
Thread} = require 'nylas-exports'
|
||||
|
||||
{Categories} = require 'nylas-observables'
|
||||
|
||||
class AccountSidebarStore extends NylasStore
|
||||
constructor: ->
|
||||
@_sections = []
|
||||
@_account = AccountStore.accounts()[0] # TODO Temporarily, should be null
|
||||
@_standardCategories = []
|
||||
@_userCategories = []
|
||||
@_registerListeners()
|
||||
@_updateSections()
|
||||
|
||||
|
@ -41,7 +44,6 @@ class AccountSidebarStore extends NylasStore
|
|||
|
||||
_registerListeners: ->
|
||||
@listenTo WorkspaceStore, @_updateSections
|
||||
@listenTo CategoryStore, @_updateSections
|
||||
@listenTo ThreadCountsStore, @_updateSections
|
||||
@listenTo FocusedPerspectiveStore, => @trigger()
|
||||
@listenTo Actions.selectAccount, @_onSelectAccount
|
||||
|
@ -50,16 +52,31 @@ class AccountSidebarStore extends NylasStore
|
|||
@_updateSections
|
||||
)
|
||||
|
||||
_registerObservables: ->
|
||||
@_disposables ?= []
|
||||
@_disposables.forEach (disp) -> disp.dispose()
|
||||
@_disposables = [
|
||||
Categories.standard(@_currentAccount).subscribe(@_onStandardCategoriesChanged),
|
||||
Categories.user(@_currentAccount).subscribe(@_onUserCategoriesChanged)
|
||||
]
|
||||
|
||||
_onSelectAccount: (accountId)=>
|
||||
@_account = AccountStore.accountForId(accountId)
|
||||
@_registerObservables()
|
||||
@trigger()
|
||||
|
||||
_onStandardCategoriesChanged: (categories) ->
|
||||
@_standardCategories = categories
|
||||
@_updateSections()
|
||||
|
||||
_onUserCategoriesChanged: (categories) ->
|
||||
@_userCategories = categories
|
||||
@_updateSections()
|
||||
|
||||
_updateSections: =>
|
||||
# TODO As it is now, if the current account is null, we will display the
|
||||
# categories for all accounts.
|
||||
# Update this to reflect UI decision for sidebar
|
||||
userCategories = CategoryStore.userCategories(@_account)
|
||||
userCategoryItems = _.map(userCategories, @_sidebarItemForCategory)
|
||||
|
||||
# Compute hierarchy for userCategoryItems using known "path" separators
|
||||
# NOTE: This code uses the fact that userCategoryItems is a sorted set, eg:
|
||||
|
@ -71,7 +88,7 @@ class AccountSidebarStore extends NylasStore
|
|||
#
|
||||
userCategoryItemsHierarchical = []
|
||||
userCategoryItemsSeen = {}
|
||||
for category in userCategories
|
||||
for category in @_userCategories
|
||||
# https://regex101.com/r/jK8cC2/1
|
||||
itemKey = category.displayName.replace(/[./\\]/g, '/')
|
||||
|
||||
|
@ -93,8 +110,7 @@ class AccountSidebarStore extends NylasStore
|
|||
|
||||
# Our drafts are displayed via the `DraftListSidebarItem` which
|
||||
# is loading into the `Drafts` Sheet.
|
||||
standardCategories = CategoryStore.standardCategories(@_account)
|
||||
standardCategories = _.reject standardCategories, (category) =>
|
||||
standardCategories = _.reject @_standardCategories, (category) =>
|
||||
category.name is "drafts"
|
||||
|
||||
standardCategoryItems = _.map standardCategories, (cat) => @_sidebarItemForCategory(cat)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
React = require 'react'
|
||||
AccountSidebarStore = require './account-sidebar-store'
|
||||
{Actions, AccountStore} = require("nylas-exports")
|
||||
crypto = require 'crypto'
|
||||
{RetinaImg} = require 'nylas-component-kit'
|
||||
crypto = require 'crypto'
|
||||
classNames = require 'classnames'
|
||||
|
||||
class AccountSwitcher extends React.Component
|
||||
|
|
|
@ -22,37 +22,55 @@ React = require 'react'
|
|||
KeyCommandsRegion,
|
||||
LabelColorizer} = require 'nylas-component-kit'
|
||||
|
||||
{Categories} = require 'nylas-observables'
|
||||
|
||||
# This changes the category on one or more threads.
|
||||
class CategoryPicker extends React.Component
|
||||
@displayName: "CategoryPicker"
|
||||
@containerRequired: false
|
||||
|
||||
constructor: (@props) ->
|
||||
@_account = AccountStore.accountForItems(@_threads(@props))
|
||||
@_categories = []
|
||||
@_standardCategories = []
|
||||
@_userCategories = []
|
||||
@state = _.extend @_recalculateState(@props), searchValue: ""
|
||||
|
||||
@contextTypes:
|
||||
sheetDepth: React.PropTypes.number
|
||||
|
||||
componentDidMount: =>
|
||||
@_registerObservables()
|
||||
|
||||
# If the threads we're picking categories for change, (like when they
|
||||
# get their categories updated), we expect our parents to pass us new
|
||||
# props. We don't listen to the DatabaseStore ourselves.
|
||||
componentWillReceiveProps: (nextProps) ->
|
||||
@_account = AccountStore.accountForItems(@_threads(nextProps))
|
||||
@_registerObservables()
|
||||
@setState @_recalculateState(nextProps)
|
||||
|
||||
componentWillUnmount: =>
|
||||
return unless @unsubscribers
|
||||
unsubscribe() for unsubscribe in @unsubscribers
|
||||
@_unregisterObservables()
|
||||
|
||||
_registerObservables: =>
|
||||
@_unregisterObservables()
|
||||
@disposables = []
|
||||
@disposables.push(
|
||||
Categories.forAccount(@_account).subscribe(@_onCategoriesChanged)
|
||||
)
|
||||
|
||||
_unregisterObservables: =>
|
||||
return unless @disposables
|
||||
disp.dispose() for disp in @disposables
|
||||
|
||||
_keymapHandlers: ->
|
||||
"application:change-category": @_onOpenCategoryPopover
|
||||
|
||||
render: =>
|
||||
return <span></span> if @state.disabled or not @_account?
|
||||
btnClasses = "btn btn-toolbar"
|
||||
btnClasses += " btn-disabled" if @state.disabled
|
||||
button = <button className={btnClasses} title={tooltip}>
|
||||
<RetinaImg name={img} mode={RetinaImg.Mode.ContentIsMask}/>
|
||||
</button>
|
||||
return button if @state.disabled or not @_account?
|
||||
|
||||
if @_account?.usesLabels()
|
||||
img = "toolbar-tag.png"
|
||||
|
@ -69,6 +87,12 @@ class CategoryPicker extends React.Component
|
|||
|
||||
if @state.isPopoverOpen then tooltip = ""
|
||||
|
||||
button = (
|
||||
<button className={btnClasses} title={tooltip}>
|
||||
<RetinaImg name={img} mode={RetinaImg.Mode.ContentIsMask}/>
|
||||
</button>
|
||||
)
|
||||
|
||||
headerComponents = [
|
||||
<input type="text"
|
||||
tabIndex="1"
|
||||
|
@ -235,10 +259,15 @@ class CategoryPicker extends React.Component
|
|||
_onPopoverClosed: =>
|
||||
@setState isPopoverOpen: false
|
||||
|
||||
_recalculateState: (props=@props, {searchValue}={}) =>
|
||||
threads = @_threads(props)
|
||||
@_account = AccountStore.accountForItems(threads)
|
||||
_onCategoriesChanged: (categories) =>
|
||||
@_categories = categories
|
||||
@_standardCategories = categories.filter (cat) -> cat.isStandardCategory()
|
||||
@_userCategories = categories.filter (cat) -> cat.isUserCategory()
|
||||
@setState @_recalculateState()
|
||||
|
||||
_recalculateState: (props = @props, {searchValue}={}) =>
|
||||
return {disabled: true} unless @_account
|
||||
threads = @_threads(props)
|
||||
|
||||
searchValue = searchValue ? @state?.searchValue ? ""
|
||||
numThreads = threads.length
|
||||
|
@ -246,11 +275,11 @@ class CategoryPicker extends React.Component
|
|||
return {categoryData: [], searchValue}
|
||||
|
||||
if @_account.usesLabels()
|
||||
categories = CategoryStore.categories(@_account)
|
||||
categories = @_categories
|
||||
else
|
||||
categories = CategoryStore.standardCategories(@_account)
|
||||
categories = @_standardCategories
|
||||
.concat([{divider: true, id: "category-divider"}])
|
||||
.concat(CategoryStore.userCategories(@_account))
|
||||
.concat(@_userCategories)
|
||||
|
||||
usageCount = @_categoryUsageCount(props, categories)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
React = require 'react/addons'
|
||||
classNames = require 'classnames'
|
||||
{Actions, WorkspaceStore} = require 'nylas-exports'
|
||||
{Actions, WorkspaceStore, FocusedMailViewStore} = require 'nylas-exports'
|
||||
{Menu, RetinaImg, KeyCommandsRegion} = require 'nylas-component-kit'
|
||||
SearchSuggestionStore = require './search-suggestion-store'
|
||||
_ = require 'underscore'
|
||||
|
|
|
@ -7,6 +7,7 @@ _ = require 'underscore'
|
|||
AccountStore,
|
||||
MutableQuerySubscription,
|
||||
QueryResultSetView,
|
||||
FocusedMailViewStore,
|
||||
DatabaseStore} = require 'nylas-exports'
|
||||
|
||||
class DraftListStore extends NylasStore
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{Category, Label} = require 'nylas-exports'
|
||||
|
||||
fdescribe 'Category', ->
|
||||
describe 'Category', ->
|
||||
|
||||
describe '_initCategoryTypes', ->
|
||||
|
||||
|
@ -22,6 +22,15 @@ fdescribe 'Category', ->
|
|||
expect(cat.isHiddenCategory()).toBe false
|
||||
expect(cat.isLockedCategory()).toBe false
|
||||
|
||||
it 'assigns type for `important` category when should not show important', ->
|
||||
cat = new Label
|
||||
cat.name = 'important'
|
||||
cat._initCategoryTypes()
|
||||
expect(cat.isUserCategory()).toBe false
|
||||
expect(cat.isStandardCategory(false)).toBe false
|
||||
expect(cat.isHiddenCategory()).toBe true
|
||||
expect(cat.isLockedCategory()).toBe false
|
||||
|
||||
it 'assigns type correctly when it is a hidden category', ->
|
||||
cat = new Label
|
||||
cat.name = 'archive'
|
||||
|
|
|
@ -44,7 +44,7 @@ class MailImportantIcon extends React.Component
|
|||
onClick={@_onToggleImportant}></div>
|
||||
|
||||
_account: =>
|
||||
AccountStore.accountForId(@state.thread.accountId)
|
||||
AccountStore.accountForId(@props.thread.accountId)
|
||||
|
||||
_onToggleImportant: (event) =>
|
||||
category = CategoryStore.getStandardCategory(@_account(), 'important')
|
||||
|
|
|
@ -95,7 +95,22 @@ class Category extends Model
|
|||
if @name in HiddenCategoryNames
|
||||
@types.push @constructor.Types.Hidden
|
||||
|
||||
# Define getter for isStandardCategory. Must take into account important
|
||||
# setting
|
||||
Object.defineProperty @, "isStandardCategory",
|
||||
enumerable: true
|
||||
configurable: true
|
||||
value: (showImportant)=>
|
||||
showImportant ?= NylasEnv.config.get('core.workspace.showImportant')
|
||||
val = @constructor.Types.Standard
|
||||
if showImportant is true
|
||||
val in @types
|
||||
else
|
||||
val in @types and @name isnt 'important'
|
||||
|
||||
# Define getters for other category types
|
||||
for key, val of @constructor.Types
|
||||
continue if val is @constructor.Types.Standard
|
||||
do (key, val) =>
|
||||
Object.defineProperty @, "is#{key}Category",
|
||||
enumerable: true
|
||||
|
|
|
@ -94,7 +94,7 @@ class Thread extends Model
|
|||
super(json)
|
||||
|
||||
# Public: Returns true if the thread has a {Category} with the given
|
||||
# name. Note, only `CategoryStore::standardCategories` have valid
|
||||
# name. Note, only catgories of type `Category.Types.Standard` have valid
|
||||
# `names`
|
||||
#
|
||||
# * `id` A {String} {Category} name
|
||||
|
|
|
@ -5,16 +5,6 @@ AccountStore = require './account-store'
|
|||
{Categories} = require 'nylas-observables'
|
||||
Rx = require 'rx-lite'
|
||||
|
||||
_observables = (account) ->
|
||||
{accountId} = account
|
||||
categories = Categories.forAccount(account).sort()
|
||||
return {
|
||||
allCategories: categories
|
||||
userCategories: categories.categoryFilter((cat) -> cat.isUserCategory())
|
||||
hiddenCategories: categories.categoryFilter((cat) -> cat.isHiddenCategory())
|
||||
standardCategories: Categories.standardForAccount(account).sort()
|
||||
}
|
||||
|
||||
class CategoryStore extends NylasStore
|
||||
|
||||
constructor: ->
|
||||
|
@ -23,29 +13,28 @@ class CategoryStore extends NylasStore
|
|||
|
||||
@listenTo AccountStore, @_onAccountsChanged
|
||||
|
||||
byId: (id) -> @_categoryCache[id]
|
||||
byId: (account, categoryId) -> @categories(account)[categoryId]
|
||||
|
||||
# Public: Returns an array of all categories for an account, both
|
||||
# standard and user generated. The items returned by this function will be
|
||||
# either {Folder} or {Label} objects.
|
||||
#
|
||||
categories: (account) ->
|
||||
@_observables[account.id].allCategories.last()
|
||||
@_categoryCache[account.id]
|
||||
|
||||
# Public: Returns all of the standard categories for the current account.
|
||||
#
|
||||
standardCategories: (account) ->
|
||||
@_observables[account.id].standardCategories.last()
|
||||
_.values(@categories(account)).filter (cat) -> cat.isStandardCategory()
|
||||
|
||||
hiddenCategories: (account) ->
|
||||
@_observables[account.id].hiddenCategories.last()
|
||||
_.values(@categories(account)).filter (cat) -> cat.isHiddenCategory()
|
||||
|
||||
# Public: Returns all of the categories that are not part of the standard
|
||||
# category set.
|
||||
#
|
||||
userCategories: (account) ->
|
||||
@_observables[account.id].userCategories.last()
|
||||
|
||||
_.values(@categories(account)).filter (cat) -> cat.isUserCategory()
|
||||
|
||||
# Public: Returns the Folder or Label object for a standard category name and
|
||||
# for a given account.
|
||||
|
@ -78,19 +67,18 @@ class CategoryStore extends NylasStore
|
|||
_onAccountsChanged: ->
|
||||
@_setupObservables(AccountStore.accounts())
|
||||
|
||||
_onCategoriesChanged: (categories) =>
|
||||
_onCategoriesChanged: (accountId, categories) =>
|
||||
return unless categories
|
||||
@_categoryCache = {}
|
||||
@_categoryCache[accountId] = {}
|
||||
for category in categories
|
||||
@_categoryCache[category.id] = category
|
||||
@_categoryCache[accountId][category.id] = category
|
||||
@trigger()
|
||||
|
||||
_setupObservables: (accounts) =>
|
||||
@_observables = {}
|
||||
accounts.forEach (account) =>
|
||||
@_observables[account.accountId] = _observables(account)
|
||||
|
||||
@_disposable?.dispose()
|
||||
@_disposable = Categories.forAllAccounts().subscribe(@_onCategoriesChanged)
|
||||
@_disposables ?= []
|
||||
@_disposables.forEach (disp) -> disp.dispose()
|
||||
@_disposables = accounts.map (account) =>
|
||||
Categories.forAccount(account)
|
||||
.subscribe(@_onCategoriesChanged.bind(@, account.id))
|
||||
|
||||
module.exports = new CategoryStore()
|
||||
|
|
|
@ -184,6 +184,7 @@ class ContactStore extends NylasStore
|
|||
return Promise.resolve(detected)
|
||||
|
||||
__refreshCache: (contacts) =>
|
||||
return unless contacts
|
||||
contacts.forEach (contact) =>
|
||||
@_contactCache[contact.accountId] ?= []
|
||||
@_contactCache[contact.accountId].push(contact)
|
||||
|
|
|
@ -107,7 +107,7 @@ class FocusedContactsStore extends NylasStore
|
|||
_calculatePenalties: (contact, score) ->
|
||||
penalties = 0
|
||||
email = contact.email.toLowerCase().trim()
|
||||
myEmail = AccountStore.accountForId(@currentThread?.accountId).emailAddress
|
||||
myEmail = AccountStore.accountForId(@currentThread?.accountId)?.emailAddress
|
||||
|
||||
if email is myEmail
|
||||
# The whole thing which will penalize to zero
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
NylasStore = require 'nylas-store'
|
||||
WorkspaceStore = require './workspace-store'
|
||||
AccountStore = require './workspace-store'
|
||||
MailboxPerspective = require '../../mailbox-perspective'
|
||||
CategoryStore = require './category-store'
|
||||
Actions = require '../actions'
|
||||
|
@ -15,8 +16,11 @@ class FocusedPerspectiveStore extends NylasStore
|
|||
_onCategoryStoreChanged: ->
|
||||
if not @_current
|
||||
@_setPerspective(@_defaultPerspective())
|
||||
else if @_current.categoryId() and not CategoryStore.byId(@_current.categoryId())
|
||||
@_setPerspective(@_defaultPerspective())
|
||||
else
|
||||
account = @_current.account
|
||||
catId = @_current.categoryId()
|
||||
if catId and not CategoryStore.byId(account, catId)
|
||||
@_setPerspective(@_defaultPerspective())
|
||||
|
||||
_onFocusMailView: (filter) =>
|
||||
return if filter.isEqual(@_current)
|
||||
|
@ -38,8 +42,12 @@ class FocusedPerspectiveStore extends NylasStore
|
|||
@_currentBeforeSearch = null
|
||||
|
||||
_defaultPerspective: ->
|
||||
# TODO what should the default mail view be
|
||||
MailboxPerspective.unified()
|
||||
# TODO Update unified MailboxPerspective
|
||||
account = AccountStore.accounts()[0]
|
||||
category = CategoryStore.getStandardCategory(account, "inbox")
|
||||
return null unless category
|
||||
MailViewFilter.forCategory(account, category)
|
||||
# MailboxPerspective.unified()
|
||||
|
||||
_setPerspective: (filter) ->
|
||||
return if filter?.isEqual(@_current)
|
||||
|
|
|
@ -21,7 +21,7 @@ class TaskFactory
|
|||
else
|
||||
labelsToRemove = []
|
||||
if exclusive
|
||||
currentLabel = CategoryStore.byId(fromView?.categoryId())
|
||||
currentLabel = CategoryStore.byId(account, fromView?.categoryId())
|
||||
currentLabel ?= CategoryStore.getStandardCategory(account, "inbox")
|
||||
labelsToRemove = [currentLabel]
|
||||
|
||||
|
@ -42,7 +42,7 @@ class TaskFactory
|
|||
else
|
||||
labelsToAdd = []
|
||||
if exclusive
|
||||
currentLabel = CategoryStore.byId(fromView?.categoryId())
|
||||
currentLabel = CategoryStore.byId(account, fromView?.categoryId())
|
||||
currentLabel ?= CategoryStore.getStandardCategory(account, "inbox")
|
||||
labelsToAdd = [currentLabel]
|
||||
|
||||
|
|
|
@ -49,26 +49,28 @@ CategoryObservables =
|
|||
_.extend(observable, CategoryOperators)
|
||||
observable
|
||||
|
||||
standardForAccount: (account) =>
|
||||
standard: (account) =>
|
||||
observable = Rx.Observable.fromConfig('core.workspace.showImportant')
|
||||
.flatMapLatest (showImportant) =>
|
||||
accountObservable = CategoryObservables.forAccount(account)
|
||||
return accountObservable.categoryFilter (cat) ->
|
||||
if showImportant is true
|
||||
cat.isStandardCategory()
|
||||
else
|
||||
cat.isStandardCategory() and cat.name isnt 'important'
|
||||
return CategoryObservables.forAccount(account).sort()
|
||||
.categoryFilter (cat) -> cat.isStandardCategory(showImportant)
|
||||
_.extend(observable, CategoryOperators)
|
||||
observable
|
||||
|
||||
user: (account) =>
|
||||
CategoryObservables.forAccount(account).sort()
|
||||
.categoryFilter (cat) -> cat.isUserCategory()
|
||||
|
||||
hidden: (account) =>
|
||||
CategoryObservables.forAccount(account).sort()
|
||||
.categoryFilter (cat) -> cat.isHiddenCategory()
|
||||
|
||||
|
||||
module.exports =
|
||||
Categories: CategoryObservables
|
||||
Accounts: AccountObservables
|
||||
|
||||
# Attach a few global helpers
|
||||
#
|
||||
Rx.Observable::last = ->
|
||||
@takeLast(1).toArray()[0]
|
||||
|
||||
Rx.Observable.fromStore = (store) =>
|
||||
return Rx.Observable.create (observer) =>
|
||||
|
|
|
@ -190,6 +190,9 @@ class CategoryMailboxPerspective extends MailboxPerspective
|
|||
|
||||
class UnifiedMailboxPerspective extends MailboxPerspective
|
||||
|
||||
categoryId: ->
|
||||
null
|
||||
|
||||
matchers: ->
|
||||
[]
|
||||
|
||||
|
|
Loading…
Reference in a new issue