diff --git a/internal_packages/message-list/lib/main.cjsx b/internal_packages/message-list/lib/main.cjsx index 4d665cf97..77bec96d7 100644 --- a/internal_packages/message-list/lib/main.cjsx +++ b/internal_packages/message-list/lib/main.cjsx @@ -7,6 +7,7 @@ SidebarThreadParticipants = require "./sidebar-thread-participants" ThreadStarButton = require './thread-star-button' ThreadArchiveButton = require './thread-archive-button' +ThreadToggleUnreadButton = require './thread-toggle-unread-button' AutolinkerExtension = require './plugins/autolinker-extension' TrackingPixelsExtension = require './plugins/tracking-pixels-extension' @@ -31,6 +32,9 @@ module.exports = ComponentRegistry.register ThreadArchiveButton, role: 'message:Toolbar' + ComponentRegistry.register ThreadToggleUnreadButton, + role: 'message:Toolbar' + MessageStore.registerExtension(AutolinkerExtension) MessageStore.registerExtension(TrackingPixelsExtension) @@ -38,6 +42,7 @@ module.exports = ComponentRegistry.unregister MessageList ComponentRegistry.unregister ThreadStarButton ComponentRegistry.unregister ThreadArchiveButton + ComponentRegistry.unregister ThreadToggleUnreadButton ComponentRegistry.unregister MessageToolbarItems ComponentRegistry.unregister SidebarThreadParticipants MessageStore.unregisterExtension(AutolinkerExtension) diff --git a/internal_packages/message-list/lib/thread-star-button.cjsx b/internal_packages/message-list/lib/thread-star-button.cjsx index fb1b55074..43568e5bb 100644 --- a/internal_packages/message-list/lib/thread-star-button.cjsx +++ b/internal_packages/message-list/lib/thread-star-button.cjsx @@ -11,7 +11,7 @@ class StarButton extends React.Component render: => selected = @props.thread? and @props.thread.starred - + + _onClick: (e) => + e.stopPropagation() + + task = new ChangeUnreadTask + thread: @props.thread + unread: !@props.thread.unread + Actions.queueTask task + Actions.popSheet() + +module.exports = ThreadToggleUnreadButton diff --git a/internal_packages/message-list/spec/message-toolbar-items-spec.cjsx b/internal_packages/message-list/spec/message-toolbar-items-spec.cjsx index a33d9f6c9..4f883dc98 100644 --- a/internal_packages/message-list/spec/message-toolbar-items-spec.cjsx +++ b/internal_packages/message-list/spec/message-toolbar-items-spec.cjsx @@ -1,9 +1,10 @@ React = require "react/addons" ReactTestUtils = React.addons.TestUtils TestUtils = React.addons.TestUtils -{Thread, FocusedContentStore, Actions} = require "nylas-exports" +{Thread, FocusedContentStore, Actions, ChangeUnreadTask} = require "nylas-exports" StarButton = require '../lib/thread-star-button' +ThreadToggleUnreadButton = require '../lib/thread-toggle-unread-button' test_thread = (new Thread).fromJSON({ "id" : "thread_12345" @@ -35,3 +36,28 @@ describe "MessageToolbarItem starring", -> expect(Actions.queueTask.mostRecentCall.args[0].threads).toEqual([test_thread_starred]) expect(Actions.queueTask.mostRecentCall.args[0].starred).toEqual(false) + +describe "MessageToolbarItem marking as unread", -> + thread = null + markUnreadBtn = null + + beforeEach -> + thread = new Thread(id: "thread-id-lol-123") + markUnreadBtn = ReactTestUtils.renderIntoDocument( + + ) + + it "queues a task to change unread status to true", -> + spyOn Actions, "queueTask" + ReactTestUtils.Simulate.click React.findDOMNode(markUnreadBtn) + + changeUnreadTask = Actions.queueTask.calls[0].args[0] + expect(changeUnreadTask instanceof ChangeUnreadTask).toBe true + expect(changeUnreadTask.unread).toBe true + expect(changeUnreadTask.threads[0].id).toBe thread.id + + it "returns to the thread list", -> + spyOn Actions, "popSheet" + ReactTestUtils.Simulate.click React.findDOMNode(markUnreadBtn) + + expect(Actions.popSheet).toHaveBeenCalled() diff --git a/internal_packages/thread-list/lib/main.cjsx b/internal_packages/thread-list/lib/main.cjsx index d86953a4c..29a7d3c00 100644 --- a/internal_packages/thread-list/lib/main.cjsx +++ b/internal_packages/thread-list/lib/main.cjsx @@ -2,7 +2,7 @@ _ = require 'underscore' React = require "react" {ComponentRegistry, WorkspaceStore} = require "nylas-exports" -{DownButton, UpButton, ThreadBulkArchiveButton, ThreadBulkStarButton} = require "./thread-buttons" +{DownButton, UpButton, ThreadBulkArchiveButton, ThreadBulkStarButton, ThreadBulkToggleUnreadButton} = require "./thread-buttons" {DraftDeleteButton} = require "./draft-buttons" ThreadSelectionBar = require './thread-selection-bar' ThreadList = require './thread-list' @@ -42,6 +42,9 @@ module.exports = ComponentRegistry.register ThreadBulkStarButton, role: 'thread:BulkAction' + ComponentRegistry.register ThreadBulkToggleUnreadButton, + role: 'thread:BulkAction' + ComponentRegistry.register DraftDeleteButton, role: 'draft:BulkAction' @@ -51,6 +54,7 @@ module.exports = ComponentRegistry.unregister ThreadList ComponentRegistry.unregister ThreadSelectionBar ComponentRegistry.unregister ThreadBulkArchiveButton + ComponentRegistry.unregister ThreadBulkToggleUnreadButton ComponentRegistry.unregister DownButton ComponentRegistry.unregister UpButton ComponentRegistry.unregister DraftDeleteButton diff --git a/internal_packages/thread-list/lib/thread-buttons.cjsx b/internal_packages/thread-list/lib/thread-buttons.cjsx index 651f1a08e..ac016ac3e 100644 --- a/internal_packages/thread-list/lib/thread-buttons.cjsx +++ b/internal_packages/thread-list/lib/thread-buttons.cjsx @@ -42,6 +42,47 @@ class ThreadBulkStarButton extends React.Component Actions.toggleStarSelection() +class ThreadBulkToggleUnreadButton extends React.Component + @displayName: 'ThreadBulkToggleUnreadButton' + @containerRequired: false + + @propTypes: + selection: React.PropTypes.object.isRequired + + constructor: -> + @state = @_getStateFromStores() + super + + componentDidMount: => + @unsubscribers = [] + @unsubscribers.push ThreadListStore.listen @_onStoreChange + + componentWillUnmount: => + unsubscribe() for unsubscribe in @unsubscribers + + render: => + fragment = if @state.canMarkUnread then "unread" else "read" + + + + _onClick: => + Actions.toggleUnreadSelection() + + _onStoreChange: => + @setState @_getStateFromStores() + + _getStateFromStores: => + selections = ThreadListStore.view().selection.items() + canMarkUnread: not selections.every (s) -> s.unread is true + + + ThreadNavButtonMixin = getInitialState: -> @_getStateFromStores() @@ -117,4 +158,4 @@ UpButton = React.createClass UpButton.containerRequired = false DownButton.containerRequired = false -module.exports = {DownButton, UpButton, ThreadBulkArchiveButton, ThreadBulkStarButton} +module.exports = {DownButton, UpButton, ThreadBulkArchiveButton, ThreadBulkStarButton, ThreadBulkToggleUnreadButton} diff --git a/internal_packages/thread-list/lib/thread-list-store.coffee b/internal_packages/thread-list/lib/thread-list-store.coffee index 883a1c308..fc6ab0c4c 100644 --- a/internal_packages/thread-list/lib/thread-list-store.coffee +++ b/internal_packages/thread-list/lib/thread-list-store.coffee @@ -9,6 +9,7 @@ NylasStore = require 'nylas-store' DatabaseStore, AccountStore, WorkspaceStore, + ChangeUnreadTask, ChangeStarredTask, FocusedContentStore, ArchiveThreadHelper, @@ -33,6 +34,8 @@ class ThreadListStore extends NylasStore @listenTo Actions.toggleStarSelection, @_onToggleStarSelection @listenTo Actions.toggleStarFocused, @_onToggleStarFocused + @listenTo Actions.toggleUnreadSelection, @_onToggleUnreadSelection + @listenTo DatabaseStore, @_onDataChanged @listenTo AccountStore, @_onAccountChanged @listenTo FocusedMailViewStore, @_onMailViewChanged @@ -146,6 +149,15 @@ class ThreadListStore extends NylasStore if task Actions.queueTask(task) + _onToggleUnreadSelection: -> + threads = @_view.selection.items() + allUnread = threads.every (t) -> + t.unread is true + unread = not allUnread + + task = new ChangeUnreadTask {threads, unread} + Actions.queueTask task + _onArchive: -> @_archiveAndShiftBy('auto') diff --git a/src/flux/actions.coffee b/src/flux/actions.coffee index 9e5332260..4cf0c7984 100644 --- a/src/flux/actions.coffee +++ b/src/flux/actions.coffee @@ -328,6 +328,7 @@ class Actions @archiveAndPrevious: ActionScopeWindow @toggleStarSelection: ActionScopeWindow @toggleStarFocused: ActionScopeWindow + @toggleUnreadSelection: ActionScopeWindow @deleteSelection: ActionScopeWindow @moveThread: ActionScopeWindow diff --git a/src/flux/tasks/change-unread-task.coffee b/src/flux/tasks/change-unread-task.coffee index 3a3d655b0..268cd5faa 100644 --- a/src/flux/tasks/change-unread-task.coffee +++ b/src/flux/tasks/change-unread-task.coffee @@ -36,6 +36,9 @@ class ChangeUnreadTask extends ChangeMailTask @threads = _.compact(threads) return super + _processesNestedMessages: -> + true + _changesToModel: (model) -> {unread: @unread} diff --git a/static/images/toolbar/icon-toolbar-markasread@2x.png b/static/images/toolbar/icon-toolbar-markasread@2x.png new file mode 100644 index 000000000..a761b2859 Binary files /dev/null and b/static/images/toolbar/icon-toolbar-markasread@2x.png differ diff --git a/static/images/toolbar/icon-toolbar-markasunread@2x.png b/static/images/toolbar/icon-toolbar-markasunread@2x.png new file mode 100644 index 000000000..192a461fe Binary files /dev/null and b/static/images/toolbar/icon-toolbar-markasunread@2x.png differ