mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-09-05 20:24:26 +08:00
feat(starred): Replace FocusedContentStore with FocusedMailViewStore, allows for starred
Summary: This diff fixes T3389 and makes it possible to define mail views which are not based on a category and focus them in the app. I think that we need to create a new index on the starred attribute to make sure the query runs fast. More tests WIP Test Plan: Run tests, more coming soon! Reviewers: dillon, evan Reviewed By: evan Maniphest Tasks: T3389 Differential Revision: https://phab.nylas.com/D1979
This commit is contained in:
parent
ee41722891
commit
1339ef19d3
21 changed files with 360 additions and 230 deletions
|
@ -62,6 +62,7 @@ class NylasExports
|
|||
@require "Calendar", 'flux/models/calendar'
|
||||
@require "Metadata", 'flux/models/metadata'
|
||||
@require "DatabaseObjectRegistry", "database-object-registry"
|
||||
@require "MailViewFilter", 'mail-view-filter'
|
||||
|
||||
# Exported so 3rd party packages can subclass Model
|
||||
@load "Model", 'flux/models/model'
|
||||
|
@ -110,7 +111,7 @@ class NylasExports
|
|||
@require "FileDownloadStore", 'flux/stores/file-download-store'
|
||||
@require "DraftStoreExtension", 'flux/stores/draft-store-extension'
|
||||
@require "FocusedContentStore", 'flux/stores/focused-content-store'
|
||||
@require "FocusedCategoryStore", 'flux/stores/focused-category-store'
|
||||
@require "FocusedMailViewStore", 'flux/stores/focused-mail-view-store'
|
||||
@require "FocusedContactsStore", 'flux/stores/focused-contacts-store'
|
||||
@require "MessageBodyProcessor", 'flux/stores/message-body-processor'
|
||||
@require "MessageStoreExtension", 'flux/stores/message-store-extension'
|
||||
|
|
|
@ -5,14 +5,18 @@ classNames = require 'classnames'
|
|||
UnreadCountStore,
|
||||
WorkspaceStore,
|
||||
AccountStore,
|
||||
FocusedCategoryStore,
|
||||
FocusedMailViewStore,
|
||||
ChangeLabelsTask,
|
||||
ChangeFolderTask,
|
||||
CategoryStore} = require 'nylas-exports'
|
||||
{RetinaImg, DropZone} = require 'nylas-component-kit'
|
||||
|
||||
class AccountSidebarCategoryItem extends React.Component
|
||||
@displayName: 'AccountSidebarCategoryItem'
|
||||
class AccountSidebarMailViewItem extends React.Component
|
||||
@displayName: 'AccountSidebarMailViewItem'
|
||||
|
||||
@propTypes:
|
||||
select: React.PropTypes.bool
|
||||
mailView: React.PropTypes.object.isRequired
|
||||
|
||||
constructor: (@props) ->
|
||||
@state =
|
||||
|
@ -32,7 +36,7 @@ class AccountSidebarCategoryItem extends React.Component
|
|||
|
||||
render: =>
|
||||
unread = []
|
||||
if @props.item.name is "inbox" and @state.unreadCount > 0
|
||||
if @props.mailView.category?.name is "inbox" and @state.unreadCount > 0
|
||||
unread = <div className="unread item-count-box">{@state.unreadCount}</div>
|
||||
|
||||
containerClass = classNames
|
||||
|
@ -42,28 +46,22 @@ class AccountSidebarCategoryItem extends React.Component
|
|||
|
||||
<DropZone className={containerClass}
|
||||
onClick={@_onClick}
|
||||
id={@props.item.id}
|
||||
id={@props.mailView.id}
|
||||
shouldAcceptDrop={@_shouldAcceptDrop}
|
||||
onDragStateChange={ ({isDropping}) => @setState({isDropping}) }
|
||||
onDrop={@_onDrop}>
|
||||
{unread}
|
||||
|
||||
<div className="icon">{@_renderIcon()}</div>
|
||||
<div className="name">{@props.item.displayName}</div>
|
||||
<div className="name">{@props.mailView.name}</div>
|
||||
</DropZone>
|
||||
|
||||
_renderIcon: ->
|
||||
if @props.sectionType is "category" and AccountStore.current()
|
||||
if AccountStore.current().usesLabels()
|
||||
<RetinaImg name={"tag.png"} mode={RetinaImg.Mode.ContentIsMask} />
|
||||
else
|
||||
<RetinaImg name={"folder.png"} mode={RetinaImg.Mode.ContentIsMask} />
|
||||
else if @props.sectionType is "mailboxes"
|
||||
<RetinaImg name={"#{@props.item.name}.png"} fallback={'folder.png'} mode={RetinaImg.Mode.ContentIsMask} />
|
||||
<RetinaImg name={@props.mailView.iconName} fallback={'folder.png'} mode={RetinaImg.Mode.ContentIsMask} />
|
||||
|
||||
_shouldAcceptDrop: (e) =>
|
||||
return false if @props.item.name in CategoryStore.LockedCategoryNames
|
||||
return false if @props.item.name is FocusedCategoryStore.categoryName()
|
||||
return false if @props.mailView.isEqual(FocusedMailViewStore.mailView())
|
||||
return false unless @props.mailView.canApplyToThreads()
|
||||
'nylas-thread-ids' in e.dataTransfer.types
|
||||
|
||||
_onDrop: (e) =>
|
||||
|
@ -71,28 +69,14 @@ class AccountSidebarCategoryItem extends React.Component
|
|||
try
|
||||
ids = JSON.parse(jsonString)
|
||||
catch err
|
||||
console.error("AccountSidebarCategoryItem onDrop: JSON parse #{err}")
|
||||
console.error("AccountSidebarMailViewItem onDrop: JSON parse #{err}")
|
||||
return unless ids
|
||||
|
||||
if AccountStore.current().usesLabels()
|
||||
currentLabel = FocusedCategoryStore.category()
|
||||
if currentLabel and not (currentLabel in CategoryStore.LockedCategoryNames)
|
||||
labelsToRemove = [currentLabel]
|
||||
|
||||
task = new ChangeLabelsTask
|
||||
threads: ids,
|
||||
labelsToAdd: [@props.item],
|
||||
labelsToRemove: labelsToRemove
|
||||
else
|
||||
task = new ChangeFolderTask
|
||||
folder: @props.item,
|
||||
threads: ids
|
||||
|
||||
Actions.queueTask(task)
|
||||
@props.mailView.applyToThreads(ids)
|
||||
|
||||
_onClick: (event) =>
|
||||
event.preventDefault()
|
||||
Actions.selectRootSheet(WorkspaceStore.Sheet.Threads)
|
||||
Actions.focusCategory(@props.item)
|
||||
Actions.focusMailView(@props.mailView)
|
||||
|
||||
module.exports = AccountSidebarCategoryItem
|
||||
module.exports = AccountSidebarMailViewItem
|
|
@ -10,7 +10,8 @@ _ = require 'underscore'
|
|||
Label,
|
||||
Folder,
|
||||
Message,
|
||||
FocusedCategoryStore,
|
||||
MailViewFilter,
|
||||
FocusedMailViewStore,
|
||||
NylasAPI,
|
||||
Thread} = require 'nylas-exports'
|
||||
|
||||
|
@ -27,7 +28,7 @@ class AccountSidebarStore extends NylasStore
|
|||
|
||||
selected: ->
|
||||
if WorkspaceStore.rootSheet() is WorkspaceStore.Sheet.Threads
|
||||
FocusedCategoryStore.category()
|
||||
FocusedMailViewStore.mailView()
|
||||
else
|
||||
WorkspaceStore.rootSheet()
|
||||
|
||||
|
@ -37,13 +38,17 @@ class AccountSidebarStore extends NylasStore
|
|||
@listenTo CategoryStore, @_refreshSections
|
||||
@listenTo WorkspaceStore, @_refreshSections
|
||||
@listenTo DraftCountStore, @_refreshSections
|
||||
@listenTo FocusedCategoryStore, => @trigger()
|
||||
@listenTo FocusedMailViewStore, => @trigger()
|
||||
|
||||
_refreshSections: =>
|
||||
account = AccountStore.current()
|
||||
return unless account
|
||||
|
||||
viewFilterForCategory = (cat) ->
|
||||
return MailViewFilter.forCategory(cat)
|
||||
|
||||
userCategories = CategoryStore.getUserCategories()
|
||||
userCategoryViews = _.map(userCategories, viewFilterForCategory)
|
||||
|
||||
# Our drafts are displayed via the `DraftListSidebarItem` which
|
||||
# is loading into the `Drafts` Sheet.
|
||||
|
@ -51,7 +56,11 @@ class AccountSidebarStore extends NylasStore
|
|||
standardCategories = _.reject standardCategories, (category) =>
|
||||
category.name is "drafts"
|
||||
|
||||
standardCategories.push(WorkspaceStore.Sheet["Drafts"])
|
||||
standardViews = _.map(standardCategories, viewFilterForCategory)
|
||||
standardViews.push(WorkspaceStore.Sheet["Drafts"])
|
||||
|
||||
starredView = MailViewFilter.forStarred()
|
||||
standardViews.splice(1, 0, starredView)
|
||||
|
||||
# Find root views, add the Views section
|
||||
# featureSheets = _.filter WorkspaceStore.Sheet, (sheet) ->
|
||||
|
@ -62,9 +71,15 @@ class AccountSidebarStore extends NylasStore
|
|||
@_sections = []
|
||||
# if featureSheets.length > 0
|
||||
# @_sections.push { label: '', items: featureSheets, type: 'sheet' }
|
||||
@_sections.push { label: 'Mailboxes', items: standardCategories, type: 'mailboxes' }
|
||||
@_sections.push
|
||||
label: 'Mailboxes'
|
||||
items: standardViews
|
||||
type: 'mailboxes'
|
||||
# @_sections.push { label: 'Views', items: extraSheets, type: 'sheet' }
|
||||
@_sections.push { label: CategoryStore.categoryLabel(), items: userCategories, type: 'category' }
|
||||
@_sections.push
|
||||
label: CategoryStore.categoryLabel()
|
||||
items: userCategoryViews
|
||||
type: 'category'
|
||||
|
||||
@trigger()
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
React = require 'react'
|
||||
{Actions, Category} = require("nylas-exports")
|
||||
{Actions, MailViewFilter} = require("nylas-exports")
|
||||
{ScrollRegion} = require("nylas-component-kit")
|
||||
SidebarDividerItem = require("./account-sidebar-divider-item")
|
||||
SidebarSheetItem = require("./account-sidebar-sheet-item")
|
||||
AccountSidebarStore = require ("./account-sidebar-store")
|
||||
AccountSidebarCategoryItem = require("./account-sidebar-category-item")
|
||||
AccountSidebarMailViewItem = require("./account-sidebar-mail-view-item")
|
||||
|
||||
class AccountSidebar extends React.Component
|
||||
@displayName: 'AccountSidebar'
|
||||
|
@ -44,18 +44,22 @@ class AccountSidebar extends React.Component
|
|||
_itemComponents: (section) =>
|
||||
section.items?.map (item) =>
|
||||
return unless item
|
||||
if item instanceof Category
|
||||
itemClass = AccountSidebarCategoryItem
|
||||
else if item.sidebarComponent
|
||||
itemClass = item.sidebarComponent
|
||||
if item instanceof MailViewFilter
|
||||
<AccountSidebarMailViewItem
|
||||
key={item.id ? item.type}
|
||||
mailView={item}
|
||||
select={ item.isEqual(@state.selected) }/>
|
||||
else
|
||||
itemClass = SidebarSheetItem
|
||||
if item.sidebarComponent
|
||||
itemClass = item.sidebarComponent
|
||||
else
|
||||
itemClass = SidebarSheetItem
|
||||
|
||||
<itemClass
|
||||
key={item.id ? item.type}
|
||||
item={item}
|
||||
sectionType={section.type}
|
||||
select={item.id is @state.selected?.id }/>
|
||||
<itemClass
|
||||
key={item.id ? item.type}
|
||||
item={item}
|
||||
sectionType={section.type}
|
||||
select={item.id is @state.selected?.id }/>
|
||||
|
||||
_onStoreChange: =>
|
||||
@setState @_getStateFromStores()
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
top:1px;
|
||||
overflow: hidden;
|
||||
padding-top: @padding-small-vertical;
|
||||
padding-bottom:@padding-small-vertical;
|
||||
line-height: @line-height-small;
|
||||
}
|
||||
&.selected {
|
||||
|
|
|
@ -2,6 +2,9 @@ React = require 'react'
|
|||
{Actions} = require("nylas-exports")
|
||||
moment = require 'moment'
|
||||
|
||||
# TODO: This file is out of date!
|
||||
return
|
||||
|
||||
class CalendarBarItem extends React.Component
|
||||
render: =>
|
||||
style =
|
||||
|
@ -28,7 +31,7 @@ class CalendarBarItem extends React.Component
|
|||
|
||||
_onClick: (event) =>
|
||||
event.preventDefault()
|
||||
Actions.focusCategory(@props.tag)
|
||||
Actions.focusMailView(@props.tag)
|
||||
|
||||
|
||||
module.exports = CalendarBarItem
|
||||
|
|
|
@ -10,7 +10,7 @@ React = require 'react'
|
|||
WorkspaceStore,
|
||||
ChangeLabelsTask,
|
||||
ChangeFolderTask,
|
||||
FocusedCategoryStore} = require 'nylas-exports'
|
||||
FocusedMailViewStore} = require 'nylas-exports'
|
||||
|
||||
{Menu,
|
||||
Popover,
|
||||
|
@ -252,7 +252,7 @@ class CategoryPicker extends React.Component
|
|||
|
||||
_isUserFacing: (allInInbox, category) =>
|
||||
hiddenCategories = []
|
||||
currentCategoryId = FocusedCategoryStore.categoryId()
|
||||
currentCategoryId = FocusedMailViewStore.mailView().categoryId()
|
||||
if @_account?.usesLabels()
|
||||
hiddenCategories = ["all", "spam", "trash", "drafts", "sent"]
|
||||
if allInInbox
|
||||
|
|
|
@ -19,16 +19,16 @@ class ThreadListIcon extends React.Component
|
|||
return 'thread-icon-star'
|
||||
|
||||
if @props.thread.unread
|
||||
return 'thread-icon-unread'
|
||||
return 'thread-icon-unread thread-icon-star-on-hover'
|
||||
|
||||
msgs = @_nonDraftMessages()
|
||||
last = msgs[msgs.length - 1]
|
||||
|
||||
if msgs.length > 1 and last.from[0]?.isMe()
|
||||
if Utils.isForwardedMessage(last)
|
||||
return 'thread-icon-forwarded'
|
||||
return 'thread-icon-forwarded thread-icon-star-on-hover'
|
||||
else
|
||||
return 'thread-icon-replied'
|
||||
return 'thread-icon-replied thread-icon-star-on-hover'
|
||||
|
||||
return 'thread-icon-star-on-hover'
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ NylasStore = require 'nylas-store'
|
|||
FocusedContentStore,
|
||||
ArchiveThreadHelper,
|
||||
TaskQueueStatusStore,
|
||||
FocusedCategoryStore} = require 'nylas-exports'
|
||||
FocusedMailViewStore} = require 'nylas-exports'
|
||||
|
||||
# Public: A mutable text container with undo/redo support and the ability
|
||||
# to annotate logical regions in the text.
|
||||
|
@ -37,7 +37,7 @@ class ThreadListStore extends NylasStore
|
|||
|
||||
@listenTo DatabaseStore, @_onDataChanged
|
||||
@listenTo AccountStore, @_onAccountChanged
|
||||
@listenTo FocusedCategoryStore, @_onCategoryChanged
|
||||
@listenTo FocusedMailViewStore, @_onMailViewChanged
|
||||
|
||||
atom.config.observe 'core.workspace.mode', => @_autofocusForLayoutMode()
|
||||
|
||||
|
@ -45,7 +45,7 @@ class ThreadListStore extends NylasStore
|
|||
# has hot yet been populated from the database with the list of
|
||||
# categories and their corresponding ids. Once that is ready, the
|
||||
# CategoryStore will trigger, which will update the
|
||||
# FocusedCategoryStore, which will cause us to create a new
|
||||
# FocusedMailViewStore, which will cause us to create a new
|
||||
# @view.
|
||||
|
||||
_resetInstanceVars: ->
|
||||
|
@ -67,23 +67,18 @@ class ThreadListStore extends NylasStore
|
|||
@trigger(@)
|
||||
|
||||
createView: ->
|
||||
categoryId = FocusedCategoryStore.categoryId()
|
||||
mailViewFilter = FocusedMailViewStore.mailView()
|
||||
account = AccountStore.current()
|
||||
return unless account
|
||||
|
||||
if @_searchQuery
|
||||
@setView(new SearchView(@_searchQuery, account.id))
|
||||
|
||||
else if account.id and categoryId
|
||||
else if account.id and mailViewFilter
|
||||
matchers = []
|
||||
matchers.push Thread.attributes.accountId.equal(account.id)
|
||||
matchers = matchers.concat(mailViewFilter.matchers())
|
||||
|
||||
if account.usesLabels()
|
||||
matchers.push Thread.attributes.labels.contains(categoryId)
|
||||
else if account.usesFolders()
|
||||
matchers.push Thread.attributes.folders.contains(categoryId)
|
||||
else
|
||||
throw new Error("Invalid organizationUnit")
|
||||
view = new DatabaseView Thread, {matchers}, (ids) =>
|
||||
DatabaseStore.findAll(Message)
|
||||
.where(Message.attributes.threadId.in(ids))
|
||||
|
@ -101,7 +96,7 @@ class ThreadListStore extends NylasStore
|
|||
|
||||
# Inbound Events
|
||||
|
||||
_onCategoryChanged: ->
|
||||
_onMailViewChanged: ->
|
||||
@createView()
|
||||
|
||||
_onAccountChanged: ->
|
||||
|
|
|
@ -10,7 +10,7 @@ classNames = require 'classnames'
|
|||
WorkspaceStore,
|
||||
AccountStore,
|
||||
CategoryStore,
|
||||
FocusedCategoryStore} = require 'nylas-exports'
|
||||
FocusedMailViewStore} = require 'nylas-exports'
|
||||
|
||||
ThreadListParticipants = require './thread-list-participants'
|
||||
ThreadListQuickActions = require './thread-list-quick-actions'
|
||||
|
@ -91,7 +91,7 @@ class ThreadList extends React.Component
|
|||
if hasAttachments
|
||||
attachment = <div className="thread-icon thread-icon-attachment"></div>
|
||||
|
||||
currentCategoryId = FocusedCategoryStore.categoryId()
|
||||
currentCategoryId = FocusedMailViewStore.mailView()?.categoryId()
|
||||
allCategoryId = CategoryStore.getStandardCategory('all')?.id
|
||||
ignoredIds = [currentCategoryId, allCategoryId]
|
||||
|
||||
|
@ -116,7 +116,7 @@ class ThreadList extends React.Component
|
|||
c5 = new ListTabular.Column
|
||||
name: "HoverActions"
|
||||
resolver: (thread) =>
|
||||
currentCategoryId = FocusedCategoryStore.categoryId()
|
||||
currentCategoryId = FocusedMailViewStore.mailView()?.categoryId()
|
||||
<ThreadListQuickActions thread={thread} categoryId={currentCategoryId}/>
|
||||
|
||||
@wideColumns = [c1, c2, c3, c4, c5]
|
||||
|
|
|
@ -261,12 +261,11 @@
|
|||
|
||||
// stars
|
||||
|
||||
.thread-list .list-item:hover .thread-icon-star-on-hover:hover {
|
||||
.thread-list .thread-icon-star:hover {
|
||||
background-image:url(../static/images/thread-list/icon-star-action-hover-@2x.png);
|
||||
background-size: 16px;
|
||||
}
|
||||
.thread-icon-star-on-hover:hover,
|
||||
.thread-list .list-item:hover .thread-icon-star-on-hover {
|
||||
.thread-list .thread-icon-star-on-hover:hover {
|
||||
background-image:url(../static/images/thread-list/icon-star-hover-@2x.png);
|
||||
background-size: 16px;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
_ = require 'underscore'
|
||||
{Thread,
|
||||
Actions,
|
||||
MailViewFilter,
|
||||
AccountStore,
|
||||
CategoryStore,
|
||||
DatabaseStore} = require 'nylas-exports'
|
||||
|
@ -44,7 +45,9 @@ module.exports =
|
|||
atom.displayWindow()
|
||||
if AccountStore.current().id isnt thread.accountId
|
||||
Actions.selectAccountId(thread.accountId)
|
||||
Actions.focusCategory(thread.categoryNamed('inbox'))
|
||||
|
||||
MailViewFilter filter = MailViewFilter.forCategory(thread.categoryNamed('inbox'))
|
||||
Actions.focusMailView(filter)
|
||||
Actions.setFocus(collection: 'thread', item: thread)
|
||||
|
||||
_notifyMessages: ->
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
_ = require 'underscore'
|
||||
|
||||
Label = require '../../src/flux/models/label'
|
||||
Folder = require '../../src/flux/models/folder'
|
||||
|
||||
CategoryStore = require '../../src/flux/stores/category-store'
|
||||
AccountStore = require '../../src/flux/stores/account-store'
|
||||
FocusedCategoryStore = require '../../src/flux/stores/focused-category-store'
|
||||
|
||||
describe "FocusedCategoryStore", ->
|
||||
beforeEach ->
|
||||
spyOn(FocusedCategoryStore, 'trigger')
|
||||
FocusedCategoryStore._category = null
|
||||
|
||||
afterEach ->
|
||||
atom.testOrganizationUnit = null
|
||||
|
||||
testStore = ->
|
||||
describe "_onCategoryStoreChanged", ->
|
||||
it "should set the current category to Inbox when it is unset", ->
|
||||
FocusedCategoryStore._category = null
|
||||
FocusedCategoryStore._onCategoryStoreChanged()
|
||||
expect(FocusedCategoryStore.category().id).toEqual(@inboxCategory.id)
|
||||
|
||||
it "should set the current category to Inbox when the current category no longer exists in the CategoryStore", ->
|
||||
otherAccountInbox = @inboxCategory.clone()
|
||||
otherAccountInbox.serverId = 'other-id'
|
||||
FocusedCategoryStore._category = otherAccountInbox
|
||||
FocusedCategoryStore._onCategoryStoreChanged()
|
||||
expect(FocusedCategoryStore.category().id).toEqual(@inboxCategory.id)
|
||||
|
||||
describe "_onSearchQueryCommitted", ->
|
||||
it "should clear the focused category and trigger when a search query is committed", ->
|
||||
FocusedCategoryStore._onFocusCategory(@userCategory)
|
||||
FocusedCategoryStore._onSearchQueryCommitted('bla')
|
||||
expect(FocusedCategoryStore.trigger).toHaveBeenCalled()
|
||||
expect(FocusedCategoryStore.category()).toBe(null)
|
||||
expect(FocusedCategoryStore.categoryName()).toBe(null)
|
||||
|
||||
it "should restore the category that was previously focused and trigger when a search query is cleared", ->
|
||||
FocusedCategoryStore._onFocusCategory(@userCategory)
|
||||
FocusedCategoryStore._onSearchQueryCommitted('bla')
|
||||
expect(FocusedCategoryStore.category()).toEqual(null)
|
||||
expect(FocusedCategoryStore.categoryName()).toEqual(null)
|
||||
FocusedCategoryStore._onSearchQueryCommitted('')
|
||||
expect(FocusedCategoryStore.trigger).toHaveBeenCalled()
|
||||
expect(FocusedCategoryStore.category().id).toEqual(@userCategory.id)
|
||||
expect(FocusedCategoryStore.categoryName()).toEqual(null)
|
||||
|
||||
describe "_onFocusCategory", ->
|
||||
it "should focus the category and trigger when Actions.focusCategory is called", ->
|
||||
FocusedCategoryStore._onFocusCategory(@userCategory)
|
||||
expect(FocusedCategoryStore.trigger).toHaveBeenCalled()
|
||||
expect(FocusedCategoryStore.categoryName()).toBe(null)
|
||||
expect(FocusedCategoryStore.category().id).toEqual(@userCategory.id)
|
||||
|
||||
it "should do nothing if the category is already focused", ->
|
||||
FocusedCategoryStore._onFocusCategory(@inboxCategory)
|
||||
spyOn(FocusedCategoryStore, '_setCategory')
|
||||
FocusedCategoryStore._onFocusCategory(@inboxCategory)
|
||||
expect(FocusedCategoryStore._setCategory).not.toHaveBeenCalled()
|
||||
|
||||
describe 'when using labels', ->
|
||||
beforeEach ->
|
||||
atom.testOrganizationUnit = 'label'
|
||||
|
||||
@inboxCategory = new Label(id: 'id-123', name: 'inbox', displayName: "INBOX")
|
||||
@userCategory = new Label(id: 'id-456', name: null, displayName: "MyCategory")
|
||||
|
||||
spyOn(CategoryStore, "getStandardCategory").andReturn @inboxCategory
|
||||
spyOn(CategoryStore, "byId").andCallFake (id) =>
|
||||
return @inboxCategory if id is @inboxCategory.id
|
||||
return @userCategory if id is @userCategory.id
|
||||
return null
|
||||
|
||||
testStore()
|
||||
|
||||
describe 'when using folders', ->
|
||||
beforeEach ->
|
||||
atom.testOrganizationUnit = 'folder'
|
||||
|
||||
@inboxCategory = new Folder(id: 'id-123', name: 'inbox', displayName: "INBOX")
|
||||
@userCategory = new Folder(id: 'id-456', name: null, displayName: "MyCategory")
|
||||
|
||||
spyOn(CategoryStore, "getStandardCategory").andReturn @inboxCategory
|
||||
|
||||
testStore()
|
89
spec-nylas/stores/focused-mail-view-store-spec.coffee
Normal file
89
spec-nylas/stores/focused-mail-view-store-spec.coffee
Normal file
|
@ -0,0 +1,89 @@
|
|||
_ = require 'underscore'
|
||||
|
||||
Label = require '../../src/flux/models/label'
|
||||
Folder = require '../../src/flux/models/folder'
|
||||
MailViewFilter = require '../../src/mail-view-filter'
|
||||
|
||||
CategoryStore = require '../../src/flux/stores/category-store'
|
||||
AccountStore = require '../../src/flux/stores/account-store'
|
||||
FocusedMailViewStore = require '../../src/flux/stores/focused-mail-view-store'
|
||||
|
||||
describe "FocusedMailViewStore", ->
|
||||
beforeEach ->
|
||||
spyOn(FocusedMailViewStore, 'trigger')
|
||||
FocusedMailViewStore._mailView = null
|
||||
|
||||
afterEach ->
|
||||
atom.testOrganizationUnit = null
|
||||
|
||||
testStore = ->
|
||||
describe "_onCategoryStoreChanged", ->
|
||||
it "should set the current category to Inbox when it is unset", ->
|
||||
FocusedMailViewStore._mailView = null
|
||||
FocusedMailViewStore._onCategoryStoreChanged()
|
||||
expect(FocusedMailViewStore.mailView()).not.toBe(null)
|
||||
expect(FocusedMailViewStore.mailView().categoryId()).toEqual(@inboxCategory.id)
|
||||
|
||||
it "should set the current category to Inbox when the current category no longer exists in the CategoryStore", ->
|
||||
otherAccountInbox = @inboxCategory.clone()
|
||||
otherAccountInbox.serverId = 'other-id'
|
||||
FocusedMailViewStore._mailView = MailViewFilter.forCategory(otherAccountInbox)
|
||||
FocusedMailViewStore._onCategoryStoreChanged()
|
||||
expect(FocusedMailViewStore.mailView().categoryId()).toEqual(@inboxCategory.id)
|
||||
|
||||
describe "_onSearchQueryCommitted", ->
|
||||
it "should clear the focused category and trigger when a search query is committed", ->
|
||||
FocusedMailViewStore._onFocusMailView(@userFilter)
|
||||
FocusedMailViewStore._onSearchQueryCommitted('bla')
|
||||
expect(FocusedMailViewStore.trigger).toHaveBeenCalled()
|
||||
expect(FocusedMailViewStore.mailView()).toBe(null)
|
||||
|
||||
it "should restore the category that was previously focused and trigger when a search query is cleared", ->
|
||||
FocusedMailViewStore._onFocusMailView(@userFilter)
|
||||
FocusedMailViewStore._onSearchQueryCommitted('bla')
|
||||
expect(FocusedMailViewStore.mailView()).toEqual(null)
|
||||
FocusedMailViewStore._onSearchQueryCommitted('')
|
||||
expect(FocusedMailViewStore.trigger).toHaveBeenCalled()
|
||||
expect(FocusedMailViewStore.mailView().categoryId()).toEqual(@userCategory.id)
|
||||
|
||||
describe "_onFocusMailView", ->
|
||||
it "should focus the category and trigger when Actions.focusCategory is called", ->
|
||||
FocusedMailViewStore._onFocusMailView(@userFilter)
|
||||
expect(FocusedMailViewStore.trigger).toHaveBeenCalled()
|
||||
expect(FocusedMailViewStore.mailView().categoryId()).toEqual(@userCategory.id)
|
||||
|
||||
it "should do nothing if the category is already focused", ->
|
||||
FocusedMailViewStore._onFocusMailView(@inboxFilter)
|
||||
spyOn(FocusedMailViewStore, '_setMailView')
|
||||
FocusedMailViewStore._onFocusMailView(@inboxFilter)
|
||||
expect(FocusedMailViewStore._setMailView).not.toHaveBeenCalled()
|
||||
|
||||
describe 'when using labels', ->
|
||||
beforeEach ->
|
||||
atom.testOrganizationUnit = 'label'
|
||||
|
||||
@inboxCategory = new Label(id: 'id-123', name: 'inbox', displayName: "INBOX")
|
||||
@inboxFilter = MailViewFilter.forCategory(@inboxCategory)
|
||||
@userCategory = new Label(id: 'id-456', name: null, displayName: "MyCategory")
|
||||
@userFilter = MailViewFilter.forCategory(@userCategory)
|
||||
|
||||
spyOn(CategoryStore, "getStandardCategory").andReturn @inboxCategory
|
||||
spyOn(CategoryStore, "byId").andCallFake (id) =>
|
||||
return @inboxCategory if id is @inboxCategory.id
|
||||
return @userCategory if id is @userCategory.id
|
||||
return null
|
||||
|
||||
testStore()
|
||||
|
||||
describe 'when using folders', ->
|
||||
beforeEach ->
|
||||
atom.testOrganizationUnit = 'folder'
|
||||
|
||||
@inboxCategory = new Folder(id: 'id-123', name: 'inbox', displayName: "INBOX")
|
||||
@inboxFilter = MailViewFilter.forCategory(@inboxCategory)
|
||||
@userCategory = new Folder(id: 'id-456', name: null, displayName: "MyCategory")
|
||||
@userFilter = MailViewFilter.forCategory(@userCategory)
|
||||
|
||||
spyOn(CategoryStore, "getStandardCategory").andReturn @inboxCategory
|
||||
|
||||
testStore()
|
|
@ -214,10 +214,10 @@ class Actions
|
|||
*Scope: Window*
|
||||
|
||||
```
|
||||
Actions.focusCategory(<Category>)
|
||||
Actions.focusMailView(<Category>)
|
||||
```
|
||||
###
|
||||
@focusCategory: ActionScopeWindow
|
||||
@focusMailView: ActionScopeWindow
|
||||
|
||||
###
|
||||
Public: If the message with the provided id is currently beign displayed in the
|
||||
|
|
|
@ -36,6 +36,7 @@ class CategoryStore extends NylasStore
|
|||
"drafts"
|
||||
"all"
|
||||
"archive"
|
||||
"starred"
|
||||
]
|
||||
|
||||
AllMailName: "all"
|
||||
|
@ -97,7 +98,7 @@ class CategoryStore extends NylasStore
|
|||
#
|
||||
getUserCategories: ->
|
||||
userCategories = _.reject _.values(@_categoryCache), (cat) =>
|
||||
cat.name in @StandardCategoryNames
|
||||
cat.name in @StandardCategoryNames or cat.name in @HiddenCategoryNames
|
||||
userCategories = _.sortBy(userCategories, 'displayName')
|
||||
return _.compact(userCategories)
|
||||
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
NylasStore = require 'nylas-store'
|
||||
CategoryStore = require './category-store'
|
||||
AccountStore = require './account-store'
|
||||
Actions = require '../actions'
|
||||
|
||||
class FocusedCategoryStore extends NylasStore
|
||||
constructor: ->
|
||||
@listenTo CategoryStore, @_onCategoryStoreChanged
|
||||
@listenTo Actions.focusCategory, @_onFocusCategory
|
||||
@listenTo Actions.searchQueryCommitted, @_onSearchQueryCommitted
|
||||
@_onCategoryStoreChanged()
|
||||
|
||||
# Inbound Events
|
||||
_onCategoryStoreChanged: ->
|
||||
if @_category?.id
|
||||
category = CategoryStore.byId(@_category.id)
|
||||
category ?= @_defaultCategory()
|
||||
@_setCategory(category)
|
||||
|
||||
_onFocusCategory: (category) ->
|
||||
return if @_category?.id is category?.id
|
||||
|
||||
if @_category is null and category
|
||||
Actions.searchQueryCommitted('')
|
||||
|
||||
@_setCategory(category)
|
||||
|
||||
_onSearchQueryCommitted: (query="") ->
|
||||
if typeof(query) != "string"
|
||||
query = query[0].all
|
||||
if query.trim().length > 0 and @_category
|
||||
@_categoryBeforeSearch = @_category
|
||||
@_setCategory(null)
|
||||
else if query.trim().length is 0
|
||||
if @_categoryBeforeSearch
|
||||
@_setCategory(@_categoryBeforeSearch)
|
||||
else
|
||||
@_setCategory(@_defaultCategory())
|
||||
|
||||
_defaultCategory: ->
|
||||
CategoryStore.getStandardCategory('inbox')
|
||||
|
||||
_setCategory: (category) ->
|
||||
return if @_category?.id is category?.id
|
||||
@_category = category
|
||||
@trigger()
|
||||
|
||||
# Public Methods
|
||||
|
||||
category: -> @_category ? null
|
||||
|
||||
categoryId: -> @_category?.id ? null
|
||||
|
||||
categoryName: -> @_category?.name ? null
|
||||
|
||||
module.exports = new FocusedCategoryStore()
|
54
src/flux/stores/focused-mail-view-store.coffee
Normal file
54
src/flux/stores/focused-mail-view-store.coffee
Normal file
|
@ -0,0 +1,54 @@
|
|||
NylasStore = require 'nylas-store'
|
||||
MailViewFilter = require '../../mail-view-filter'
|
||||
CategoryStore = require './category-store'
|
||||
AccountStore = require './account-store'
|
||||
Actions = require '../actions'
|
||||
|
||||
class FocusedMailViewStore extends NylasStore
|
||||
constructor: ->
|
||||
@listenTo CategoryStore, @_onCategoryStoreChanged
|
||||
@listenTo Actions.focusMailView, @_onFocusMailView
|
||||
@listenTo Actions.searchQueryCommitted, @_onSearchQueryCommitted
|
||||
@_onCategoryStoreChanged()
|
||||
|
||||
# Inbound Events
|
||||
_onCategoryStoreChanged: ->
|
||||
if not @_mailView
|
||||
@_setMailView(@_defaultMailView())
|
||||
else
|
||||
if not CategoryStore.byId(@_mailView.categoryId())
|
||||
@_setMailView(@_defaultMailView())
|
||||
|
||||
_onFocusMailView: (filter) ->
|
||||
return if filter.isEqual(@_mailView)
|
||||
if @_mailView is null and filter
|
||||
Actions.searchQueryCommitted('')
|
||||
@_setMailView(filter)
|
||||
|
||||
_onSearchQueryCommitted: (query="") ->
|
||||
if typeof(query) != "string"
|
||||
query = query[0].all
|
||||
if query.trim().length > 0 and @_mailView
|
||||
@_mailViewBeforeSearch = @_mailView
|
||||
@_setMailView(null)
|
||||
else if query.trim().length is 0
|
||||
if @_mailViewBeforeSearch
|
||||
@_setMailView(@_mailViewBeforeSearch)
|
||||
else
|
||||
@_setMailView(@_defaultMailView())
|
||||
|
||||
_defaultMailView: ->
|
||||
category = CategoryStore.getStandardCategory('inbox')
|
||||
return null unless category
|
||||
MailViewFilter.forCategory(category)
|
||||
|
||||
_setMailView: (filter) ->
|
||||
return if filter?.isEqual(@_mailView)
|
||||
@_mailView = filter
|
||||
@trigger()
|
||||
|
||||
# Public Methods
|
||||
|
||||
mailView: -> @_mailView ? null
|
||||
|
||||
module.exports = new FocusedMailViewStore()
|
|
@ -1,5 +1,5 @@
|
|||
CategoryStore = require '../stores/category-store'
|
||||
FocusedCategoryStore = require '../stores/focused-category-store'
|
||||
FocusedMailViewStore = require '../stores/focused-mail-view-store'
|
||||
|
||||
ChangeLabelsTask = require './change-labels-task'
|
||||
ChangeFolderTask = require './change-folder-task'
|
||||
|
@ -48,7 +48,8 @@ class ArchiveThreadHelper
|
|||
threads: threads
|
||||
|
||||
else if account.usesLabels()
|
||||
currentLabel = FocusedCategoryStore.category()
|
||||
viewCategoryId = FocusedMailViewStore.mailView().categoryId()
|
||||
currentLabel = CategoryStore.byId(viewCategoryId)
|
||||
currentLabel ?= CategoryStore.getStandardCategory("inbox")
|
||||
|
||||
params = {threads}
|
||||
|
|
123
src/mail-view-filter.coffee
Normal file
123
src/mail-view-filter.coffee
Normal file
|
@ -0,0 +1,123 @@
|
|||
_ = require 'underscore'
|
||||
|
||||
AccountStore = require './flux/stores/account-store'
|
||||
CategoryStore = require './flux/stores/category-store'
|
||||
Thread = require './flux/models/thread'
|
||||
Actions = require './flux/actions'
|
||||
|
||||
# This is a class cluster. Subclasses are not for external use!
|
||||
# https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html
|
||||
|
||||
class MailViewFilter
|
||||
|
||||
# Factory Methods
|
||||
|
||||
@forCategory: (category) ->
|
||||
new CategoryMailViewFilter(category)
|
||||
|
||||
@forStarred: ->
|
||||
new StarredMailViewFilter()
|
||||
|
||||
# Instance Methods
|
||||
|
||||
constructor: ->
|
||||
|
||||
isEqual: (other) ->
|
||||
return false unless other and @constructor.name is other.constructor.name
|
||||
return false if other.name isnt @name
|
||||
|
||||
matchers = @matchers()
|
||||
otherMatchers = other.matchers()
|
||||
return false if otherMatchers.length isnt matchers.length
|
||||
|
||||
for idx in [0...matchers.length]
|
||||
if matchers[idx].value() isnt otherMatchers[idx].value()
|
||||
return false
|
||||
|
||||
true
|
||||
|
||||
categoryId: ->
|
||||
throw new Error("categoryId: Not implemented in base class.")
|
||||
|
||||
matchers: ->
|
||||
throw new Error("matchers: Not implemented in base class.")
|
||||
|
||||
canApplyToThreads: ->
|
||||
throw new Error("canApplyToThreads: Not implemented in base class.")
|
||||
|
||||
applyToThreads: (threadsOrIds) ->
|
||||
throw new Error("applyToThreads: Not implemented in base class.")
|
||||
|
||||
|
||||
class StarredMailViewFilter extends MailViewFilter
|
||||
constructor: ->
|
||||
@name = "Starred"
|
||||
@iconName = "starred.png"
|
||||
@
|
||||
|
||||
matchers: ->
|
||||
[Thread.attributes.starred.equal(true)]
|
||||
|
||||
categoryId: ->
|
||||
null
|
||||
|
||||
canApplyToThreads: ->
|
||||
true
|
||||
|
||||
applyToThreads: (threadsOrIds) ->
|
||||
ChangeStarredTask = require './flux/tasks/change-starred-task'
|
||||
task = new ChangeStarredTask({threads:threadsOrIds, starred: true})
|
||||
Actions.queueTask(task)
|
||||
|
||||
|
||||
class CategoryMailViewFilter extends MailViewFilter
|
||||
constructor: (cat) ->
|
||||
@name = cat.displayName
|
||||
@category = cat
|
||||
|
||||
if cat.name
|
||||
@iconName = "#{cat.name}.png"
|
||||
else if AccountStore.current().usesLabels()
|
||||
@iconName = "tag.png"
|
||||
else
|
||||
@iconName = "folder.png"
|
||||
|
||||
@
|
||||
|
||||
matchers: ->
|
||||
account = AccountStore.current()
|
||||
matchers = []
|
||||
if account.usesLabels()
|
||||
matchers.push Thread.attributes.labels.contains(@category.id)
|
||||
else if account.usesFolders()
|
||||
matchers.push Thread.attributes.folders.contains(@category.id)
|
||||
matchers
|
||||
|
||||
categoryId: ->
|
||||
@category.id
|
||||
|
||||
canApplyToThreads: ->
|
||||
not (@category.name in CategoryStore.LockedCategoryNames)
|
||||
|
||||
applyToThreads: (threadsOrIds) ->
|
||||
if AccountStore.current().usesLabels()
|
||||
FocusedMailViewStore = require './flux/stores/focused-mail-view-store'
|
||||
currentLabel = FocusedMailViewStore.mailView().category
|
||||
if currentLabel and not (currentLabel in CategoryStore.LockedCategoryNames)
|
||||
labelsToRemove = [currentLabel]
|
||||
|
||||
ChangeLabelsTask = require './flux/tasks/change-labels-task'
|
||||
task = new ChangeLabelsTask
|
||||
threads: threadsOrIds
|
||||
labelsToAdd: [@category]
|
||||
labelsToRemove: labelsToRemove
|
||||
else
|
||||
ChangeFolderTask = require './flux/tasks/change-folder-task'
|
||||
task = new ChangeFolderTask
|
||||
threads: threadsOrIds
|
||||
folder: @category
|
||||
|
||||
Actions.queueTask(task)
|
||||
|
||||
|
||||
module.exports = MailViewFilter
|
|
@ -34,7 +34,7 @@ class WindowTitle extends React.Component
|
|||
<div className="window-title">{@state.title}</div>
|
||||
|
||||
CategoryStore = null
|
||||
FocusedCategoryStore = null
|
||||
FocusedMailViewStore = null
|
||||
class ToolbarBack extends React.Component
|
||||
@displayName: 'ToolbarBack'
|
||||
|
||||
|
@ -42,13 +42,13 @@ class ToolbarBack extends React.Component
|
|||
# This is because loading these stores has database side effects.
|
||||
constructor: (@props) ->
|
||||
CategoryStore ?= require './flux/stores/category-store'
|
||||
FocusedCategoryStore ?= require './flux/stores/focused-category-store'
|
||||
FocusedMailViewStore ?= require './flux/stores/focused-mail-view-store'
|
||||
@state =
|
||||
categoryName: FocusedCategoryStore.categoryName()
|
||||
categoryName: FocusedMailViewStore.mailView().name
|
||||
|
||||
componentDidMount: =>
|
||||
@_unsubscriber = FocusedCategoryStore.listen =>
|
||||
@setState(categoryName: FocusedCategoryStore.categoryName())
|
||||
@_unsubscriber = FocusedMailViewStore.listen =>
|
||||
@setState(categoryName: FocusedMailViewStore.mailView().name)
|
||||
|
||||
componentWillUnmount: =>
|
||||
@_unsubscriber() if @_unsubscriber
|
||||
|
|
Loading…
Add table
Reference in a new issue