diff --git a/exports/nylas-component-kit.coffee b/exports/nylas-component-kit.coffee index 1dc50aedb..4bcd80e63 100644 --- a/exports/nylas-component-kit.coffee +++ b/exports/nylas-component-kit.coffee @@ -11,7 +11,6 @@ module.exports = Popover: require '../src/components/popover' Flexbox: require '../src/components/flexbox' RetinaImg: require '../src/components/retina-img' - EmptyState: require '../src/components/empty-state' ListTabular: require '../src/components/list-tabular' DraggableImg: require '../src/components/draggable-img' MultiselectList: require '../src/components/multiselect-list' diff --git a/internal_packages/message-list/lib/message-list.cjsx b/internal_packages/message-list/lib/message-list.cjsx index 6b1ffeaf5..e38499899 100755 --- a/internal_packages/message-list/lib/message-list.cjsx +++ b/internal_packages/message-list/lib/message-list.cjsx @@ -293,6 +293,7 @@ class MessageList extends React.Component _scrollToBottom: => messageWrap = React.findDOMNode(@refs.messageWrap) + return unless messageWrap messageWrap.scrollTop = messageWrap.scrollHeight _cacheScrollPos: => diff --git a/internal_packages/search-bar/stylesheets/search-bar.less b/internal_packages/search-bar/stylesheets/search-bar.less index f0595515b..c9fbd0290 100644 --- a/internal_packages/search-bar/stylesheets/search-bar.less +++ b/internal_packages/search-bar/stylesheets/search-bar.less @@ -73,7 +73,7 @@ &.clear { position: absolute; - top: floor(40px - 26px)/2 - 1px; + top: 4px; color: @input-cancel-color; right: @padding-base-horizontal; display: none; diff --git a/static/images/empty-state/blank-bottom-left@2x.png b/internal_packages/thread-list/assets/blank-bottom-left@2x.png similarity index 100% rename from static/images/empty-state/blank-bottom-left@2x.png rename to internal_packages/thread-list/assets/blank-bottom-left@2x.png diff --git a/static/images/empty-state/blank-bottom-right@2x.png b/internal_packages/thread-list/assets/blank-bottom-right@2x.png similarity index 100% rename from static/images/empty-state/blank-bottom-right@2x.png rename to internal_packages/thread-list/assets/blank-bottom-right@2x.png diff --git a/static/images/empty-state/blank-top-left@2x.png b/internal_packages/thread-list/assets/blank-top-left@2x.png similarity index 100% rename from static/images/empty-state/blank-top-left@2x.png rename to internal_packages/thread-list/assets/blank-top-left@2x.png diff --git a/static/images/empty-state/blank-top-right@2x.png b/internal_packages/thread-list/assets/blank-top-right@2x.png similarity index 100% rename from static/images/empty-state/blank-top-right@2x.png rename to internal_packages/thread-list/assets/blank-top-right@2x.png diff --git a/internal_packages/thread-list/lib/empty-state.cjsx b/internal_packages/thread-list/lib/empty-state.cjsx new file mode 100644 index 000000000..b3b91117e --- /dev/null +++ b/internal_packages/thread-list/lib/empty-state.cjsx @@ -0,0 +1,130 @@ +_ = require 'underscore' +React = require 'react' +classNames = require 'classnames' +{RetinaImg} = require 'nylas-component-kit' +{DatabaseView, + NamespaceStore, + NylasAPI, + WorkspaceStore} = require 'nylas-exports' + +EmptyMessages = [{ + "body":"The pessimist complains about the wind.\nThe optimist expects it to change.\nThe realist adjusts the sails." + "byline": "- William Arthur Ward" +},{ + "body":"The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart." + "byline": "- Hellen Keller" +},{ + "body":"Believe you can and you're halfway there." + "byline": "- Theodore Roosevelt" +},{ + "body":"Don't judge each day by the harvest you reap but by the seeds that you plant." + "byline": "- Robert Louis Stevenson" +}] + +class ContentGeneric extends React.Component + render: -> +
+
+ {@props.messageOverride ? "No threads to display."} +
+
+ +class ContentQuotes extends React.Component + @displayName = 'Quotes' + + constructor: (@props) -> + @state = {} + + componentDidMount: -> + # Pick a random quote using the day as a seed. I know not all months have + # 31 days - this is good enough to generate one quote a day at random! + d = new Date() + r = d.getDate() + d.getMonth() * 31 + message = EmptyMessages[r % EmptyMessages.length] + @setState(message: message) + + render: -> +
+ {@_renderMessage()} + + + + +
+ + _renderMessage: -> + if @props.messageOverride +
{@props.messageOverride}
+ else +
+ {@state.message?.body} +
+ {@state.message?.byline} +
+
+ + +class EmptyState extends React.Component + @displayName = 'EmptyState' + @propTypes = + visible: React.PropTypes.bool.isRequired + dataView: React.PropTypes.object + + constructor: (@props) -> + @state = + layoutMode: WorkspaceStore.layoutMode() + syncing: false + active: false + + componentDidMount: -> + @_unlisteners = [] + @_unlisteners.push WorkspaceStore.listen(@_onChange, @) + @_unlisteners.push NamespaceStore.listen(@_onNamespacesChanged, @) + @_onNamespacesChanged() + + _onNamespacesChanged: -> + namespace = NamespaceStore.current() + @_worker = NylasAPI.workerForNamespace(namespace) + @_workerUnlisten() if @_workerUnlisten + @_workerUnlisten = @_worker.listen(@_onChange, @) + console.log(@_worker) + @setState(syncing: @_worker.busy()) + + componentWillUnmount: -> + unlisten() for unlisten in @_unlisteners + @_workerUnlisten() if @_workerUnlisten + + componentDidUpdate: -> + if @props.visible and not @state.active + @setState(active:true) + + componentWillReceiveProps: (newProps) -> + if newProps.visible is false + @setState(active:false) + + render: -> + ContentComponent = ContentGeneric + messageOverride = null + + if @props.dataView instanceof DatabaseView + if @state.layoutMode is 'list' + ContentComponent = ContentQuotes + if @state.syncing + messageOverride = "Please wait while we prepare your mailbox." + + classes = classNames + 'empty-state': true + 'visible': @props.visible + 'active': @state.active + +
+ +
+ + _onChange: -> + @setState + layoutMode: WorkspaceStore.layoutMode() + syncing: @_worker.busy() + + +module.exports = EmptyState diff --git a/internal_packages/thread-list/lib/thread-list.cjsx b/internal_packages/thread-list/lib/thread-list.cjsx index 8b5f1fc30..50c895305 100644 --- a/internal_packages/thread-list/lib/thread-list.cjsx +++ b/internal_packages/thread-list/lib/thread-list.cjsx @@ -14,6 +14,8 @@ ThreadListQuickActions = require './thread-list-quick-actions' ThreadListStore = require './thread-list-store' ThreadListIcon = require './thread-list-icon' +EmptyState = require './empty-state' + class ThreadListScrollTooltip extends React.Component @displayName: 'ThreadListScrollTooltip' @propTypes: @@ -128,6 +130,7 @@ class ThreadList extends React.Component 'application:reply': @_onReply 'application:reply-all': @_onReplyAll 'application:forward': @_onForward + @itemPropsProvider = (item) -> className: classNames 'unread': item.isUnread() @@ -149,6 +152,7 @@ class ThreadList extends React.Component itemHeight={39} className="thread-list" scrollTooltipComponent={ThreadListScrollTooltip} + emptyComponent={EmptyState} collection="thread" /> else if @state.style is 'narrow' else
diff --git a/static/components/empty-state.less b/internal_packages/thread-list/stylesheets/empty-state.less similarity index 83% rename from static/components/empty-state.less rename to internal_packages/thread-list/stylesheets/empty-state.less index a8d70308b..45472086d 100644 --- a/static/components/empty-state.less +++ b/internal_packages/thread-list/stylesheets/empty-state.less @@ -13,10 +13,33 @@ overflow:hidden; > div { - opacity:0; + opacity: 0; -webkit-transition: opacity @duration ease-out; width:100%; height: 100%; + } + + // Generic Mode + .generic { + text-align: center; + + .message { + color: @text-color-very-subtle; + font-size: 28px; + font-weight: @font-weight-blond; + text-align: center; + top:45%; + left:50%; + width:80%; + transform: translate(-50%, -50%); + position: absolute; + white-space: pre-line; + } + } + + // Quotes Mode + + .quotes { min-width: 840px; min-height: 650px; position: absolute; @@ -113,4 +136,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/components/empty-state.cjsx b/src/components/empty-state.cjsx deleted file mode 100644 index 179a83438..000000000 --- a/src/components/empty-state.cjsx +++ /dev/null @@ -1,64 +0,0 @@ -_ = require 'underscore' -React = require 'react' -classNames = require 'classnames' -RetinaImg = require './retina-img' - -EmptyMessages = [{ - "body":"The pessimist complains about the wind.\nThe optimist expects it to change.\nThe realist adjusts the sails." - "byline": "- William Arthur Ward" -},{ - "body":"The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart." - "byline": "- Hellen Keller" -},{ - "body":"Believe you can and you're halfway there." - "byline": "- Theodore Roosevelt" -},{ - "body":"Don't judge each day by the harvest you reap but by the seeds that you plant." - "byline": "- Robert Louis Stevenson" -}] - -class EmptyState extends React.Component - @displayName = 'EmptyState' - @propTypes = - visible: React.PropTypes.bool.isRequired - - constructor: (@props) -> - @state = - active: false - - componentDidUpdate: -> - if @props.visible and not @state.active - # Pick a random quote using the day as a seed. I know not all months have - # 31 days - this is good enough to generate one quote a day at random! - d = new Date() - r = d.getDate() + d.getMonth() * 31 - message = EmptyMessages[r % EmptyMessages.length] - @setState(active:true, message: message) - - componentWillReceiveProps: (newProps) -> - if newProps.visible is false - @setState(active:false) - - render: -> - classes = classNames - 'empty-state': true - 'visible': @props.visible - 'active': @state.active - -
-
-
- {@state.message?.body} -
- {@state.message?.byline} -
-
- - - - -
-
- - -module.exports = EmptyState diff --git a/src/components/multiselect-list.cjsx b/src/components/multiselect-list.cjsx index d0a2c07ed..d398c9bfe 100644 --- a/src/components/multiselect-list.cjsx +++ b/src/components/multiselect-list.cjsx @@ -2,7 +2,6 @@ _ = require 'underscore' React = require 'react' classNames = require 'classnames' ListTabular = require './list-tabular' -EmptyState = require './empty-state' Spinner = require './spinner' {Actions, Utils, @@ -37,6 +36,7 @@ class MultiselectList extends React.Component itemPropsProvider: React.PropTypes.func.isRequired itemHeight: React.PropTypes.number.isRequired scrollTooltipComponent: React.PropTypes.func + emptyComponent: React.PropTypes.func constructor: (@props) -> @state = @_getStateFromStores() @@ -107,6 +107,12 @@ class MultiselectList extends React.Component 'keyboard-cursor': @state.handler.shouldShowKeyboardCursor() and item.id is @state.keyboardCursorId props + emptyElement = [] + if @props.emptyComponent + emptyElement = <@props.emptyComponent + visible={@state.ready && @state.dataView.count() is 0} + dataView={@state.dataView} /> + if @state.dataView
- + {emptyElement}
else
diff --git a/src/flux/nylas-sync-worker.coffee b/src/flux/nylas-sync-worker.coffee index e3d572c20..aba68cdb8 100644 --- a/src/flux/nylas-sync-worker.coffee +++ b/src/flux/nylas-sync-worker.coffee @@ -33,11 +33,17 @@ class NylasSyncWorker state: -> @_state + busy: -> + for key, state of @_state + if state.busy + return true + false + start: -> @_resumeTimer = setInterval(@resumeFetches, 20000) @_connection.start() @resumeFetches() - + cleanup: -> clearInterval(@_resumeTimer) @_connection.end() @@ -64,7 +70,7 @@ class NylasSyncWorker @fetchCollectionCount(model) @fetchCollectionPage(model, {offset: 0, limit: PAGE_SIZE}) - + fetchCollectionCount: (model) -> @_api.makeRequest path: "/n/#{@_namespaceId}/#{model}" diff --git a/src/flux/stores/workspace-store.coffee b/src/flux/stores/workspace-store.coffee index eac392e34..2d5e4710b 100644 --- a/src/flux/stores/workspace-store.coffee +++ b/src/flux/stores/workspace-store.coffee @@ -187,8 +187,9 @@ class WorkspaceStore # Return to the root sheet. This method triggers, allowing observers # to update. popToRootSheet: => - @_sheetStack.length = 1 - @trigger() + if @_sheetStack.length > 1 + @_sheetStack.length = 1 + @trigger() triggerDebounced: _.debounce(( -> @trigger(@)), 1) diff --git a/static/index.less b/static/index.less index 875ca8b59..e11a7fac4 100644 --- a/static/index.less +++ b/static/index.less @@ -21,5 +21,4 @@ @import "components/scroll-region"; @import "components/spinner"; @import "components/generated-form"; -@import "components/empty-state"; @import "components/unsafe";