feat(archive): shows trash for non-archive accounts

Summary:
Fixes T3570
Fixes T3737

Rename ArchiveThreadHelper to RemoveThreadHelper

Rename of `getRemovalTask`

Remove unarchive from `RemoveThreadHelper`. Pass in mailviewfilter

Rename actions

Rename archive action

renmaing

support trash and archive folders in RemoveThreadHelper

Move everything over to trash

add tests

Hide trash and archive

Test Plan: new tests

Reviewers: dillon, bengotow

Reviewed By: dillon, bengotow

Maniphest Tasks: T3570, T3737

Differential Revision: https://phab.nylas.com/D2089
This commit is contained in:
Evan Morikawa 2015-09-30 19:51:48 -07:00
parent b1724b9e47
commit f88b0f3708
17 changed files with 316 additions and 204 deletions

View file

@ -90,7 +90,6 @@ class NylasExports
@require "ChangeStarredTask", 'flux/tasks/change-starred-task'
@require "CreateMetadataTask", 'flux/tasks/create-metadata-task'
@require "MarkMessageReadTask", 'flux/tasks/mark-message-read'
@require "ArchiveThreadHelper", 'flux/tasks/archive-thread-helper'
@require "DestroyMetadataTask", 'flux/tasks/destroy-metadata-task'
# Stores
@ -134,6 +133,7 @@ class NylasExports
@load "UndoManager", 'flux/undo-manager'
@load "QuotedHTMLParser", 'services/quoted-html-parser'
@load "QuotedPlainTextParser", 'services/quoted-plain-text-parser'
@require "RemoveThreadHelper", 'services/remove-thread-helper'
# Errors
@get "APIError", -> require('../src/flux/errors').APIError

View file

@ -9,7 +9,7 @@ MessageToolbarItems = require "./message-toolbar-items"
SidebarContactList} = require "./sidebar-components"
ThreadStarButton = require './thread-star-button'
ThreadArchiveButton = require './thread-archive-button'
ThreadRemoveButton = require './thread-remove-button'
ThreadToggleUnreadButton = require './thread-toggle-unread-button'
AutolinkerExtension = require './plugins/autolinker-extension'
@ -36,7 +36,7 @@ module.exports =
ComponentRegistry.register ThreadStarButton,
role: 'message:Toolbar'
ComponentRegistry.register ThreadArchiveButton,
ComponentRegistry.register ThreadRemoveButton,
role: 'message:Toolbar'
ComponentRegistry.register ThreadToggleUnreadButton,
@ -48,7 +48,7 @@ module.exports =
deactivate: ->
ComponentRegistry.unregister MessageList
ComponentRegistry.unregister ThreadStarButton
ComponentRegistry.unregister ThreadArchiveButton
ComponentRegistry.unregister ThreadRemoveButton
ComponentRegistry.unregister ThreadToggleUnreadButton
ComponentRegistry.unregister MessageToolbarItems
ComponentRegistry.unregister SidebarContactCard

View file

@ -1,24 +0,0 @@
_ = require 'underscore'
React = require 'react'
{Actions, DOMUtils} = require 'nylas-exports'
{RetinaImg} = require 'nylas-component-kit'
class ThreadArchiveButton extends React.Component
@displayName: "ThreadArchiveButton"
@containerRequired: false
render: =>
<button className="btn btn-toolbar btn-archive"
style={order: -106}
data-tooltip="Archive"
onClick={@_onArchive}>
<RetinaImg name="toolbar-archive.png" mode={RetinaImg.Mode.ContentIsMask}/>
</button>
_onArchive: (e) =>
return unless DOMUtils.nodeIsVisible(e.currentTarget)
Actions.archive()
e.stopPropagation()
module.exports = ThreadArchiveButton

View file

@ -0,0 +1,37 @@
_ = require 'underscore'
React = require 'react'
{Actions,
DOMUtils,
RemoveThreadHelper,
FocusedMailViewStore} = require 'nylas-exports'
{RetinaImg} = require 'nylas-component-kit'
class ThreadRemoveButton extends React.Component
@displayName: "ThreadRemoveButton"
@containerRequired: false
render: =>
focusedMailViewFilter = FocusedMailViewStore.mailView()
return false unless focusedMailViewFilter?.canRemoveThreads()
if RemoveThreadHelper.removeType() is RemoveThreadHelper.Type.Archive
tooltip = "Archive"
imgName = "toolbar-archive.png"
else if RemoveThreadHelper.removeType() is RemoveThreadHelper.Type.Trash
tooltip = "Trash"
imgName = "toolbar-trash.png"
<button className="btn btn-toolbar"
style={order: -106}
data-tooltip={tooltip}
onClick={@_onRemove}>
<RetinaImg name={imgName} mode={RetinaImg.Mode.ContentIsMask}/>
</button>
_onRemove: (e) =>
return unless DOMUtils.nodeIsVisible(e.currentTarget)
Actions.removeCurrentlyFocusedThread()
e.stopPropagation()
module.exports = ThreadRemoveButton

View file

@ -2,7 +2,7 @@ _ = require 'underscore'
React = require "react"
{ComponentRegistry, WorkspaceStore} = require "nylas-exports"
{DownButton, UpButton, ThreadBulkArchiveButton, ThreadBulkStarButton, ThreadBulkToggleUnreadButton} = require "./thread-buttons"
{DownButton, UpButton, ThreadBulkRemoveButton, ThreadBulkStarButton, ThreadBulkToggleUnreadButton} = require "./thread-buttons"
{DraftDeleteButton} = require "./draft-buttons"
ThreadSelectionBar = require './thread-selection-bar'
ThreadList = require './thread-list'
@ -44,7 +44,7 @@ module.exports =
location: WorkspaceStore.Sheet.Thread.Toolbar.Right
modes: ['list']
ComponentRegistry.register ThreadBulkArchiveButton,
ComponentRegistry.register ThreadBulkRemoveButton,
role: 'thread:BulkAction'
ComponentRegistry.register ThreadBulkStarButton,
@ -61,7 +61,7 @@ module.exports =
ComponentRegistry.unregister DraftSelectionBar
ComponentRegistry.unregister ThreadList
ComponentRegistry.unregister ThreadSelectionBar
ComponentRegistry.unregister ThreadBulkArchiveButton
ComponentRegistry.unregister ThreadBulkRemoveButton
ComponentRegistry.unregister ThreadBulkToggleUnreadButton
ComponentRegistry.unregister DownButton
ComponentRegistry.unregister UpButton

View file

@ -2,25 +2,38 @@ React = require "react/addons"
classNames = require 'classnames'
ThreadListStore = require './thread-list-store'
{RetinaImg} = require 'nylas-component-kit'
{Actions, FocusedContentStore} = require "nylas-exports"
{Actions,
RemoveThreadHelper,
FocusedContentStore,
FocusedMailViewStore} = require "nylas-exports"
class ThreadBulkArchiveButton extends React.Component
@displayName: 'ThreadBulkArchiveButton'
class ThreadBulkRemoveButton extends React.Component
@displayName: 'ThreadBulkRemoveButton'
@containerRequired: false
@propTypes:
selection: React.PropTypes.object.isRequired
render: ->
focusedMailViewFilter = FocusedMailViewStore.mailView()
return false unless focusedMailViewFilter?.canRemoveThreads()
if RemoveThreadHelper.removeType() is RemoveThreadHelper.Type.Archive
tooltip = "Archive"
imgName = "toolbar-archive.png"
else if RemoveThreadHelper.removeType() is RemoveThreadHelper.Type.Trash
tooltip = "Trash"
imgName = "toolbar-trash.png"
<button style={order:-106}
className="btn btn-toolbar"
data-tooltip="Archive"
onClick={@_onArchive}>
<RetinaImg name="toolbar-archive.png" mode={RetinaImg.Mode.ContentIsMask} />
data-tooltip={tooltip}
onClick={@_onRemove}>
<RetinaImg name={imgName} mode={RetinaImg.Mode.ContentIsMask} />
</button>
_onArchive: =>
Actions.archiveSelection()
_onRemove: =>
Actions.removeSelection()
class ThreadBulkStarButton extends React.Component
@ -158,4 +171,4 @@ UpButton = React.createClass
UpButton.containerRequired = false
DownButton.containerRequired = false
module.exports = {DownButton, UpButton, ThreadBulkArchiveButton, ThreadBulkStarButton, ThreadBulkToggleUnreadButton}
module.exports = {DownButton, UpButton, ThreadBulkRemoveButton, ThreadBulkStarButton, ThreadBulkToggleUnreadButton}

View file

@ -1,71 +1,30 @@
_ = require 'underscore'
React = require 'react'
{Actions,
Utils,
Thread,
ArchiveThreadHelper,
CategoryStore,
ChangeFolderTask,
ChangeLabelsTask,
AccountStore} = require 'nylas-exports'
RemoveThreadHelper,
FocusedMailViewStore} = require 'nylas-exports'
class ThreadListQuickActions extends React.Component
@displayName: 'ThreadListQuickActions'
@propTypes:
thread: React.PropTypes.object
categoryId: React.PropTypes.string
render: =>
actions = []
if @_shouldDisplayArchiveButton()
actions.push <div key="archive" className="btn action action-archive" onClick={@_onArchive}></div>
else if AccountStore.current().usesLabels() and @props.categoryId == CategoryStore.getStandardCategory('all').id
actions.push <div key="trash" className="btn action action-trash" onClick={@_onTrash}></div>
focusedMailViewFilter = FocusedMailViewStore.mailView()
return false unless focusedMailViewFilter?.canRemoveThreads()
return false if actions.length is 0
classNames = "btn action action-#{RemoveThreadHelper.removeType()}"
<div className="inner">
{actions}
<div key="remove" className={classNames} onClick={@_onRemove}></div>
</div>
shouldComponentUpdate: (newProps, newState) ->
newProps.thread.id isnt @props?.thread.id
_shouldDisplayArchiveButton: =>
if @props.categoryId not in [CategoryStore.getStandardCategory('archive')?.id, CategoryStore.getStandardCategory('trash')?.id, CategoryStore.getStandardCategory('sent')?.id]
if AccountStore.current().usesLabels()
if @props.thread.labels.length == 1 and (@props.thread.labels[0].name == "archive" or @props.thread.labels[0].name == "all")
return false
return true
else if @props.thread.folders.length == 1 and @props.thread.folders[0].name == "archive"
return false
return true
return false
_onTrash: (event) =>
params =
thread: @props.thread,
labelsToRemove: [CategoryStore.byId(@props.categoryId)],
labelsToAdd: [CategoryStore.getStandardCategory("trash")]
Actions.queueTask(new ChangeLabelsTask(params))
# Don't trigger the thread row click
event.stopPropagation()
_onForward: (event) =>
Actions.composeForward({thread: @props.thread, popout: true})
# Don't trigger the thread row click
event.stopPropagation()
_onReply: (event) =>
Actions.composeReply({thread: @props.thread, popout: true})
# Don't trigger the thread row click
event.stopPropagation()
_onArchive: (event) =>
archiveTask = ArchiveThreadHelper.getArchiveTask([@props.thread])
Actions.queueTask(archiveTask)
_onRemove: (event) =>
focusedMailViewFilter = FocusedMailViewStore.mailView()
t = RemoveThreadHelper.getRemovalTask([@props.thread], focusedMailViewFilter)
Actions.queueTask(t)
# Don't trigger the thread row click
event.stopPropagation()

View file

@ -12,7 +12,7 @@ NylasStore = require 'nylas-store'
ChangeUnreadTask,
ChangeStarredTask,
FocusedContentStore,
ArchiveThreadHelper,
RemoveThreadHelper,
TaskQueueStatusStore,
FocusedMailViewStore} = require 'nylas-exports'
@ -22,14 +22,14 @@ class ThreadListStore extends NylasStore
constructor: ->
@_resetInstanceVars()
@listenTo Actions.archiveAndPrevious, @_onArchiveAndPrev
@listenTo Actions.archiveAndNext, @_onArchiveAndNext
@listenTo Actions.removeSelection, @_onRemoveSelection
@listenTo Actions.archiveSelection, @_onArchiveSelection
@listenTo Actions.moveThreads, @_onMoveThreads
@listenTo Actions.removeCurrentlyFocusedThread, @_onRemoveAndAuto
@listenTo Actions.removeAndNext, @_onRemoveAndNext
@listenTo Actions.removeAndPrevious, @_onRemoveAndPrev
@listenTo Actions.archive, @_onArchive
@listenTo Actions.moveThread, @_onMoveThread
@listenTo Actions.moveThreads, @_onMoveThreads
@listenTo Actions.toggleStarSelection, @_onToggleStarSelection
@listenTo Actions.toggleStarFocused, @_onToggleStarFocused
@ -158,24 +158,29 @@ class ThreadListStore extends NylasStore
task = new ChangeUnreadTask {threads, unread}
Actions.queueTask task
_onArchive: ->
@_archiveAndShiftBy('auto')
_onRemoveAndAuto: ->
@_removeAndShiftBy('auto')
_onArchiveAndPrev: ->
@_archiveAndShiftBy(-1)
_onRemoveAndPrev: ->
@_removeAndShiftBy(-1)
_onArchiveAndNext: ->
@_archiveAndShiftBy(1)
_onRemoveAndNext: ->
@_removeAndShiftBy(1)
_archiveAndShiftBy: (offset) ->
_removeAndShiftBy: (offset) ->
mailViewFilter = FocusedMailViewStore.mailView()
return unless mailViewFilter.canApplyToThreads()
focused = FocusedContentStore.focused('thread')
return unless focused
task = ArchiveThreadHelper.getArchiveTask([focused])
task = RemoveThreadHelper.getRemovalTask([focused], mailViewFilter)
@_moveAndShiftBy(offset, task)
_onArchiveSelection: ->
_onRemoveSelection: ->
mailViewFilter = FocusedMailViewStore.mailView()
return unless mailViewFilter.canApplyToThreads()
selectedThreads = @_view.selection.items()
task = ArchiveThreadHelper.getArchiveTask(selectedThreads)
return unless selectedThreads.length > 0
task = RemoveThreadHelper.getRemovalTask(selectedThreads, mailViewFilter)
@_onMoveThreads(selectedThreads, task)
_onMoveThread: (thread, task) ->
@ -233,13 +238,13 @@ class ThreadListStore extends NylasStore
# Remove the current thread from selection
@_view.selection.remove(focused)
# If the user is in list mode and archived without specifically saying
# "archive and next" or "archive and prev", return to the thread list
# If the user is in list mode and removed without specifically saying
# "remove and next" or "remove and prev", return to the thread list
# instead of focusing on the next message.
if layoutMode is 'list' and not explicitOffset
nextFocus = null
# Archive the current thread
# Remove the current thread
TaskQueueStatusStore.waitForPerformLocal(task).then =>
Actions.setFocus(collection: 'thread', item: nextFocus)
Actions.setCursorPosition(collection: 'thread', item: nextKeyboard)

View file

@ -133,8 +133,7 @@ class ThreadList extends React.Component
c5 = new ListTabular.Column
name: "HoverActions"
resolver: (thread) =>
currentCategoryId = FocusedMailViewStore.mailView()?.categoryId()
<ThreadListQuickActions thread={thread} categoryId={currentCategoryId}/>
<ThreadListQuickActions thread={thread} />
@wideColumns = [c1, c2, c3, c4, c5]
@ -167,10 +166,10 @@ class ThreadList extends React.Component
@narrowColumns = [cNarrow]
@commands =
'core:remove-item': @_onArchive
'core:remove-item': @_onRemoveItem
'core:star-item': @_onStarItem
'core:remove-and-previous': -> Actions.archiveAndPrevious()
'core:remove-and-next': -> Actions.archiveAndNext()
'core:remove-and-previous': -> Actions.removeAndPrevious()
'core:remove-and-next': -> Actions.removeAndNext()
@itemPropsProvider = (item) ->
className: classNames
@ -257,15 +256,15 @@ class ThreadList extends React.Component
else
Actions.toggleStarFocused()
_onArchive: =>
_onRemoveItem: =>
return unless ThreadListStore.view()
if WorkspaceStore.layoutMode() is "list" and WorkspaceStore.topSheet() is WorkspaceStore.Sheet.Thread
Actions.archive()
Actions.removeCurrentlyFocusedThread()
else if ThreadListStore.view().selection.count() > 0
Actions.archiveSelection()
Actions.removeSelection()
else
Actions.archive()
Actions.removeCurrentlyFocusedThread()
module.exports = ThreadList

View file

@ -216,9 +216,9 @@ describe "ThreadList", ->
spyOn(ThreadStore, "_onAccountChanged")
spyOn(DatabaseStore, "findAll").andCallFake ->
new Promise (resolve, reject) -> resolve(test_threads())
spyOn(Actions, "archive")
spyOn(Actions, "archiveAndNext")
spyOn(Actions, "archiveAndPrevious")
spyOn(Actions, "removeCurrentlyFocusedThread")
spyOn(Actions, "removeAndNext")
spyOn(Actions, "removeAndPrevious")
ReactTestUtils.spyOnClass(ThreadList, "_prepareColumns").andCallFake ->
@_columns = columns

View file

@ -0,0 +1,88 @@
Account = require '../../src/flux/models/account'
CategoryStore = require '../../src/flux/stores/category-store'
RemoveThreadHelper = require '../../src/services/remove-thread-helper'
ChangeFolderTask = require '../../src/flux/tasks/change-folder-task'
ChangeLabelsTask = require '../../src/flux/tasks/change-labels-task'
describe "RemoveThreadHelper", ->
describe "removeType", ->
it "returns null if there's no current account", ->
spyOn(RemoveThreadHelper, "_currentAccount").andReturn null
expect(RemoveThreadHelper.removeType()).toBe null
it "returns the type if it's saved", ->
spyOn(atom.config, "get").andReturn "trash"
expect(RemoveThreadHelper.removeType()).toBe "trash"
it "returns the archive category if it exists", ->
spyOn(CategoryStore, "getStandardCategory").andReturn {name: "archive"}
expect(RemoveThreadHelper.removeType()).toBe "archive"
it "defaults to archive for Gmail", ->
spyOn(RemoveThreadHelper, "_currentAccount").andReturn provider: "gmail"
expect(RemoveThreadHelper.removeType()).toBe "archive"
it "defaults to trash for everything else", ->
spyOn(RemoveThreadHelper, "_currentAccount").andReturn provider: "eas"
expect(RemoveThreadHelper.removeType()).toBe "trash"
describe "getRemovalTask", ->
beforeEach ->
spyOn(CategoryStore, "byId").andReturn({id: "inbox-id", name: "inbox"})
@mailViewFilterStub = categoryId: -> "inbox-id"
@categories = []
spyOn(CategoryStore, "getStandardCategory").andCallFake (cat) =>
if cat in @categories
return {id: "cat-id", name: cat}
else return null
afterEach ->
atom.testOrganizationUnit = null
it "returns null if there's no current account", ->
spyOn(RemoveThreadHelper, "_currentAccount").andReturn null
expect(RemoveThreadHelper.getRemovalTask()).toBe null
it "creates the task when using labels and trashing", ->
atom.testOrganizationUnit = "label"
spyOn(RemoveThreadHelper, "_currentAccount").andReturn new Account
provider: "eas"
organizationUnit: "label"
@categories = ["all", "trash"]
t = RemoveThreadHelper.getRemovalTask([], @mailViewFilterStub)
expect(t instanceof ChangeLabelsTask).toBe true
expect(t.labelsToRemove[0].name).toBe "inbox"
expect(t.labelsToAdd[0].name).toBe "trash"
it "creates the task when using labels and archiving", ->
@categories = ["all", "archive", "trash"]
atom.testOrganizationUnit = "label"
spyOn(RemoveThreadHelper, "_currentAccount").andReturn new Account
provider: "gmail"
organizationUnit: "label"
t = RemoveThreadHelper.getRemovalTask([], @mailViewFilterStub)
expect(t instanceof ChangeLabelsTask).toBe true
expect(t.labelsToRemove[0].name).toBe "inbox"
expect(t.labelsToAdd[0].name).toBe "all"
it "creates the task when using folders and trashing", ->
@categories = ["all", "trash"]
atom.testOrganizationUnit = "folder"
spyOn(RemoveThreadHelper, "_currentAccount").andReturn new Account
provider: "eas"
organizationUnit: "folder"
t = RemoveThreadHelper.getRemovalTask([], @mailViewFilterStub)
expect(t instanceof ChangeFolderTask).toBe true
expect(t.folder.name).toBe "trash"
it "creates the task when using folders and archiving", ->
@categories = ["all", "archive", "trash"]
atom.testOrganizationUnit = "folder"
spyOn(RemoveThreadHelper, "_currentAccount").andReturn new Account
provider: "gmail"
organizationUnit: "folder"
t = RemoveThreadHelper.getRemovalTask([], @mailViewFilterStub)
expect(t instanceof ChangeFolderTask).toBe true
expect(t.folder.name).toBe "archive"

View file

@ -31,7 +31,7 @@ The MultiselectActionBar uses the `ComponentRegistry` to find items to display f
collection name. To add an item to the bar created in the example above, register it like this:
```coffee
ComponentRegistry.register ThreadBulkArchiveButton,
ComponentRegistry.register ThreadBulkRemoveButton,
role: 'thread:BulkAction'
```

View file

@ -32,7 +32,7 @@ how it propogates between windows.
## Firing Actions
```coffee
Actions.postNotification({message: "Archived Thread", type: 'success'})
Actions.postNotification({message: "Removed Thread", type: 'success'})
Actions.queueTask(new ChangeStarredTask(thread: @_thread, starred: true))
```
@ -305,20 +305,20 @@ class Actions
@destroyDraft: ActionScopeWindow
###
Public: Archive the currently focused {Thread}.
Public: Remove the currently focused {Thread}.
*Scope: Window*
###
@archive: ActionScopeWindow
@removeCurrentlyFocusedThread: ActionScopeWindow
###
Public: Archives the Thread objects currently selected in the app's main thread list.
Public: Removes the Thread objects currently selected in the app's main thread list.
*Scope: Window*
###
@archiveSelection: ActionScopeWindow
@archiveAndNext: ActionScopeWindow
@archiveAndPrevious: ActionScopeWindow
@removeSelection: ActionScopeWindow
@removeAndNext: ActionScopeWindow
@removeAndPrevious: ActionScopeWindow
@toggleStarSelection: ActionScopeWindow
@toggleStarFocused: ActionScopeWindow
@toggleUnreadSelection: ActionScopeWindow
@ -367,7 +367,7 @@ class Actions
```
# A simple notification
Actions.postNotification({message: "Archived Thread", type: 'success'})
Actions.postNotification({message: "Removed Thread", type: 'success'})
# A sticky notification with actions
NOTIF_ACTION_YES = 'YES'

View file

@ -1,72 +0,0 @@
CategoryStore = require '../stores/category-store'
FocusedMailViewStore = require '../stores/focused-mail-view-store'
ChangeLabelsTask = require './change-labels-task'
ChangeFolderTask = require './change-folder-task'
Actions = require '../actions'
AccountStore = require '../stores/account-store'
class ArchiveThreadHelper
getArchiveTask: (threads) ->
@_getTask(threads, "archive")
getUnarchiveTask: (threads) ->
@_getTask(threads, "unarchive")
_getTask: (threads=[], direction) ->
threads = [threads] unless threads instanceof Array
account = AccountStore.current()
return null unless account
if account.usesFolders()
if direction is "archive"
archiveFolder = CategoryStore.getStandardCategory("archive")
if archiveFolder
return new ChangeFolderTask
folder: archiveFolder
threads: threads
else
# TODO: Implement some sort of UI for people to pick the folder
# they want to use as the Archive. Or better yet, automatically
# add an `Archive` folder first, then move it to there and maybe
# throw up some sort of notifciation.
#
# In the meantime, just throw up a notification so people do it on
# the backend.
Actions.postNotification
type: 'error'
tag: 'noArchive'
sticky: true
message: "You have not created an Archive folder. Please create a folder called 'Archive' with your email provider, restart N1, then try again.",
return null
else if direction is "unarchive"
inboxFolder = CategoryStore.getStandardCategory("inbox")
return new ChangeFolderTask
folder: inboxFolder
threads: threads
else if account.usesLabels()
viewCategoryId = FocusedMailViewStore.mailView().categoryId()
currentLabel = CategoryStore.byId(viewCategoryId)
currentLabel ?= CategoryStore.getStandardCategory("inbox")
params = {threads}
if direction is "archive"
params.labelsToRemove = [currentLabel]
else if direction is "unarchive"
params.labelsToAdd = [currentLabel]
archiveLabel = CategoryStore.getStandardCategory("all")
if archiveLabel
if direction is "archive"
params.labelsToAdd = [archiveLabel]
else if direction is "unarchive"
params.labelsToRemove = [archiveLabel]
return new ChangeLabelsTask(params)
else
throw new Error("Invalid organizationUnit")
module.exports = new ArchiveThreadHelper()

View file

@ -48,10 +48,13 @@ class MailViewFilter
canApplyToThreads: ->
throw new Error("canApplyToThreads: Not implemented in base class.")
# Whether or not the current MailViewFilter can "archive" or "trash"
canRemoveThreads: ->
throw new Error("canRemoveThreads: Not implemented in base class.")
applyToThreads: (threadsOrIds) ->
throw new Error("applyToThreads: Not implemented in base class.")
class SearchMailViewFilter extends MailViewFilter
constructor: (@searchQuery) ->
@
@ -65,6 +68,9 @@ class SearchMailViewFilter extends MailViewFilter
canApplyToThreads: ->
false
canRemoveThreads: ->
false
categoryId: ->
null
@ -84,6 +90,9 @@ class StarredMailViewFilter extends MailViewFilter
canApplyToThreads: ->
true
canRemoveThreads: ->
true
applyToThreads: (threadsOrIds) ->
ChangeStarredTask = require './flux/tasks/change-starred-task'
task = new ChangeStarredTask({threads:threadsOrIds, starred: true})
@ -119,6 +128,11 @@ class CategoryMailViewFilter extends MailViewFilter
canApplyToThreads: ->
not (@category.name in CategoryStore.LockedCategoryNames)
canRemoveThreads: ->
return false if @category.name in ["archive", "trash", "sent", "all"]
return false if @category.displayName is atom.config.get("core.archiveFolder")
return true
applyToThreads: (threadsOrIds) ->
if AccountStore.current().usesLabels()
FocusedMailViewStore = require './flux/stores/focused-mail-view-store'

View file

@ -0,0 +1,93 @@
_ = require 'underscore'
CategoryStore = require '../stores/category-store'
ChangeLabelsTask = require './change-labels-task'
ChangeFolderTask = require './change-folder-task'
Actions = require '../actions'
AccountStore = require '../stores/account-store'
class RemoveThreadHelper
Type:
Trash: "trash"
Archive: "archive"
removeType: ->
currentAccount = @_currentAccount()
return null unless currentAccount
savedType = atom.config.get("core.#{currentAccount.id}.removeType")
return savedType if savedType
archiveCategory = CategoryStore.getStandardCategory("archive")
return @Type.Archive if archiveCategory
if currentAccount.provider is "gmail"
return @Type.Archive
else
return @Type.Trash
_currentAccount: -> AccountStore.current() # To stub in testing
# In the case of folders, "removing" means moving the message to a
# particular folder
removalFolder: ->
if @removeType() is @Type.Trash
CategoryStore.getStandardCategory("trash")
else if @removeType() is @Type.Archive
CategoryStore.getStandardCategory("archive")
# In the case of labels, "removing" means removing the current label and
# applying a new label indicating it's in the "removed" state.
removalLabelToAdd: ->
if @removeType() is @Type.Trash
CategoryStore.getStandardCategory("trash")
else if @removeType() is @Type.Archive
CategoryStore.getStandardCategory("all")
getRemovalTask: (threads=[], focusedMailViewFilter) ->
threads = [threads] unless threads instanceof Array
account = @_currentAccount()
return null unless account
if account.usesFolders()
removalFolder = @removalFolder()
if removalFolder
return new ChangeFolderTask
folder: removalFolder
threads: threads
else
@_notifyFolderRemovalError()
return null
else if account.usesLabels()
viewCategoryId = focusedMailViewFilter.categoryId()
currentLabel = CategoryStore.byId(viewCategoryId)
currentLabel ?= CategoryStore.getStandardCategory("inbox")
params = {threads}
params.labelsToRemove = [currentLabel]
removalLabelToAdd = @removalLabelToAdd()
if removalLabelToAdd
params.labelsToAdd = [removalLabelToAdd]
return new ChangeLabelsTask(params)
else
throw new Error("Invalid organizationUnit")
_notifyFolderRemovalError: ->
# In the onboarding flow, users should have already created their
# Removal folder. This should only happen for legacy users or if
# there's an error somewhere.
if @removeType() is @Type.Trash
msg = "There is no Trash folder. Please create a folder called 'Trash' and try again."
else if @removeType() is @Type.Archive
msg = "We can't archive your messages because you have no 'Archive' folder. Please create a folder called 'Archive' and try again"
Actions.postNotification
type: 'error'
tag: 'noRemovalFolder'
sticky: true
message: msg
module.exports = new RemoveThreadHelper()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB