diff --git a/internal_packages/thread-list/lib/thread-list.cjsx b/internal_packages/thread-list/lib/thread-list.cjsx
index fefefdd58..515b63c94 100644
--- a/internal_packages/thread-list/lib/thread-list.cjsx
+++ b/internal_packages/thread-list/lib/thread-list.cjsx
@@ -57,6 +57,7 @@ class ThreadList extends React.Component
constructor: (@props) ->
@state =
style: 'unknown'
+ task: null
componentWillMount: =>
c1 = new ListTabular.Column
@@ -169,33 +170,42 @@ class ThreadList extends React.Component
render: =>
if @state.style is 'wide'
-
+
+
+
else if @state.style is 'narrow'
-
+
+
+
else
+ # _renderUndoRedo: =>
+ # if @state.task
+ #
+
_threadIdAtPoint: (x, y) ->
item = document.elementFromPoint(event.clientX, event.clientY).closest('.list-item')
return null unless item
diff --git a/internal_packages/undo-redo/lib/main.cjsx b/internal_packages/undo-redo/lib/main.cjsx
new file mode 100644
index 000000000..c450c6fe0
--- /dev/null
+++ b/internal_packages/undo-redo/lib/main.cjsx
@@ -0,0 +1,13 @@
+{ComponentRegistry, WorkspaceStore} = require 'nylas-exports'
+
+module.exports =
+ activate: (@state={}) ->
+ UndoRedoComponent = require "./undo-redo-component"
+
+ ComponentRegistry.register UndoRedoComponent,
+ location: WorkspaceStore.Location.ThreadList
+
+ deactivate: ->
+ ComponentRegistry.unregister UndoRedoComponent
+
+ serialize: -> @state
\ No newline at end of file
diff --git a/internal_packages/undo-redo/lib/undo-redo-component.cjsx b/internal_packages/undo-redo/lib/undo-redo-component.cjsx
new file mode 100644
index 000000000..734b54dda
--- /dev/null
+++ b/internal_packages/undo-redo/lib/undo-redo-component.cjsx
@@ -0,0 +1,90 @@
+_ = require 'underscore'
+path = require 'path'
+React = require 'react'
+classNames = require 'classnames'
+{RetinaImg, TimeoutTransitionGroup} = require 'nylas-component-kit'
+{Actions,
+Utils,
+ComponentRegistry,
+UndoRedoStore,
+NamespaceStore} = require 'nylas-exports'
+
+class UndoRedoComponent extends React.Component
+ @displayName: 'UndoRedoComponent'
+
+ @propTypes:
+ task: React.PropTypes.object.isRequired
+ show: React.PropTypes.bool
+
+ constructor: (@props) ->
+ @state = @_getStateFromStores()
+ @_timeout = null
+
+ _onChange: =>
+ @setState(@_getStateFromStores(), =>
+ @_setNewTimeout())
+
+ _clearTimeout: =>
+ clearTimeout(@_timeout)
+
+ _setNewTimeout: =>
+ clearTimeout(@_timeout)
+ @_timeout = setTimeout (=>
+ @_hide()
+ return
+ ), 3000
+
+ _getStateFromStores: ->
+ t = UndoRedoStore.getMostRecentTask()
+ s = false
+ if t
+ s = true
+
+ return {show: s, task: t}
+
+ componentWillMount: ->
+ @unsub = UndoRedoStore.listen(@_onChange)
+
+ componentWillUnmount: ->
+ @unsub()
+
+ render: =>
+ items = [].concat(@_renderUndoRedoManager())
+
+ names = classNames
+ "undo-redo-manager": true
+
+
+ {items}
+
+
+ _renderUndoRedoManager: =>
+ if @state.show
+
+ else
+ []
+
+ _undoTask: =>
+ atom.commands.dispatch(document.querySelector('body'), 'core:undo')
+ @_hide()
+
+ _formatMessage: =>
+ return @state.task.description()
+
+ _hide: =>
+ @setState({show: false, task: null})
+
+module.exports = UndoRedoComponent
diff --git a/internal_packages/undo-redo/package.json b/internal_packages/undo-redo/package.json
new file mode 100644
index 000000000..d5522323c
--- /dev/null
+++ b/internal_packages/undo-redo/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "undo-redo",
+ "version": "0.1.0",
+ "main": "./lib/main",
+ "description": "Undo modal button",
+ "license": "Proprietary",
+ "private": true,
+ "engines": {
+ "atom": "*"
+ },
+ "dependencies": {
+ }
+}
\ No newline at end of file
diff --git a/internal_packages/undo-redo/stylesheets/undo-redo.less b/internal_packages/undo-redo/stylesheets/undo-redo.less
new file mode 100644
index 000000000..c720eadc5
--- /dev/null
+++ b/internal_packages/undo-redo/stylesheets/undo-redo.less
@@ -0,0 +1,53 @@
+@import "ui-variables";
+@import "ui-mixins";
+
+.undo-redo{
+ position: absolute;
+ height: 45px;
+ bottom: 10px;
+ left: 39%;
+ padding-top: 12px;
+ opacity: 0.9;
+ border-radius: @border-radius-base;
+ background: @gray-darker;
+
+ .undo-redo-message-wrapper{
+ display: inline-block;
+ margin-left: 15px;
+ margin-right: 30px;
+
+ .undo-redo-message{
+ color: lighten(@gray-base, 70%);;
+ }
+ }
+
+ .undo-redo-action-wrapper{
+ float: right;
+ display: inline-block;
+ margin-right: 15px;
+
+ .undo-redo-action-text{
+ margin-left: 5px;
+ display: inline-block;
+ color: @white;
+ }
+ }
+}
+
+.undo-redo-item-enter {
+ opacity: 0.01;
+ transition: all .3s ease-out;
+}
+
+.undo-redo-item-enter.undo-redo-item-enter-active {
+ opacity: 1;
+}
+
+.undo-redo-item-leave {
+ opacity: 1;
+ transition: all .3s ease-in;
+}
+
+.undo-redo-item-leave.undo-redo-item-leave-active {
+ opacity: 0.01;
+}
\ No newline at end of file
diff --git a/src/flux/stores/undo-redo-store.coffee b/src/flux/stores/undo-redo-store.coffee
index 018ca8519..41c52ca19 100644
--- a/src/flux/stores/undo-redo-store.coffee
+++ b/src/flux/stores/undo-redo-store.coffee
@@ -25,11 +25,12 @@ class UndoRedoStore
if task.canBeUndone() and not task.isUndo()
@_redo = []
@_undo.push(task)
+ @trigger() unless task._isReverting
undo: =>
topTask = @_undo.pop()
return unless topTask
-
+ @trigger()
Actions.queueTask(topTask.createUndoTask())
@_redo.push(topTask.createIdenticalTask())
@@ -38,6 +39,10 @@ class UndoRedoStore
return unless redoTask
Actions.queueTask(redoTask)
+ getMostRecentTask: =>
+ for idx in [@_undo.length-1...-1]
+ return @_undo[idx] unless @_undo[idx]._isReverting
+
print: ->
console.log("Undo Stack")
console.log(@_undo)
diff --git a/src/flux/tasks/change-folder-task.coffee b/src/flux/tasks/change-folder-task.coffee
index 962368f42..2654a23f3 100644
--- a/src/flux/tasks/change-folder-task.coffee
+++ b/src/flux/tasks/change-folder-task.coffee
@@ -23,6 +23,19 @@ class ChangeFolderTask extends ChangeCategoryTask
label: -> "Moving to folder…"
+ description: ->
+ folderName = @folderOrId.displayName
+ if @threadIds.length > 0
+ if @threadIds.length > 1
+ return "Moving " + @threadIds.length + " threads to \"" + folderName + "\""
+ return "Moving 1 thread to \"" + folderName + "\""
+ else if @messageIds.length > 0
+ if @messageIds.length > 1
+ return "Moving " + @messageIds.length + "messages to \"" + folderName + "\""
+ return "Moving 1 message to \"" + folderName + "\""
+ else
+ return "Moving objects to \"" + folderName + "\""
+
collectCategories: ->
if @folderOrId instanceof Folder
return Promise.props
diff --git a/src/flux/tasks/change-labels-task.coffee b/src/flux/tasks/change-labels-task.coffee
index 0471a93ab..eb472b6cf 100644
--- a/src/flux/tasks/change-labels-task.coffee
+++ b/src/flux/tasks/change-labels-task.coffee
@@ -23,6 +23,12 @@ class ChangeLabelsTask extends ChangeCategoryTask
label: -> "Applying labels…"
+ description: ->
+ addingMessage = "Adding " + @labelsToAdd.length + " labels"
+ removingMessage = "Removing " + @labelsToRemove.length + " labels"
+
+ return addingMessage + " " + removingMessage
+
collectCategories: ->
labelOrIdPromiseMapper = (labelOrId) ->
if labelOrId instanceof Label
diff --git a/src/flux/tasks/update-nylas-objects-task.coffee b/src/flux/tasks/update-nylas-objects-task.coffee
index 91ffe37e3..6a6d73bcf 100644
--- a/src/flux/tasks/update-nylas-objects-task.coffee
+++ b/src/flux/tasks/update-nylas-objects-task.coffee
@@ -20,6 +20,10 @@ class UpdateNylasObjectsTask extends Task
endpoint: (obj) ->
inflection.pluralize(obj.constructor.name.toLowerCase())
+ # OVERRIDE ME
+ description: ->
+ 'Updating Nylas object'
+
performLocal: ({reverting}={}) ->
if reverting or @isUndo()
Promise.map @objects, (obj) =>
diff --git a/src/task.coffee b/src/task.coffee
index 080c0b61e..9bf460cf2 100644
--- a/src/task.coffee
+++ b/src/task.coffee
@@ -134,6 +134,11 @@ class Task
throw new Error('Cannot send message to terminated process')
undefined
+ # Public: Describe the function of the task. Each task should override this
+ # to explain its individual function
+ description: ->
+ ''
+
# Public: Call a function when an event is emitted by the child process
#
# * `eventName` The {String} name of the event to handle.
diff --git a/static/images/thread-list/undo-icon@2x.png b/static/images/thread-list/undo-icon@2x.png
new file mode 100644
index 000000000..c0b85e8b1
Binary files /dev/null and b/static/images/thread-list/undo-icon@2x.png differ