diff --git a/internal_packages/notifications/lib/activity-sidebar.cjsx b/internal_packages/notifications/lib/activity-sidebar.cjsx
index 896d9d1a1..baeacc85c 100644
--- a/internal_packages/notifications/lib/activity-sidebar.cjsx
+++ b/internal_packages/notifications/lib/activity-sidebar.cjsx
@@ -2,12 +2,12 @@ React = require 'react'
_ = require 'underscore'
classNames = require 'classnames'
NotificationStore = require './notifications-store'
+InitialSyncActivity = require './initial-sync-activity'
{Actions,
TaskQueue,
AccountStore,
NylasSyncStatusStore,
- TaskQueueStatusStore,
- NylasAPI} = require 'nylas-exports'
+ TaskQueueStatusStore} = require 'nylas-exports'
ActivitySidebarLongPollStore = require './activity-sidebar-long-poll-store'
{TimeoutTransitionGroup, RetinaImg} = require 'nylas-component-kit'
@@ -25,8 +25,8 @@ class ActivitySidebar extends React.Component
componentDidMount: =>
@_unlisteners = []
@_unlisteners.push TaskQueueStatusStore.listen @_onDataChanged
- @_unlisteners.push NylasSyncStatusStore.listen @_onDataChanged
@_unlisteners.push NotificationStore.listen @_onDataChanged
+ @_unlisteners.push NylasSyncStatusStore.listen @_onDataChanged
@_unlisteners.push ActivitySidebarLongPollStore.listen @_onDeltaReceived
componentWillUnmount: =>
@@ -34,10 +34,14 @@ class ActivitySidebar extends React.Component
@_workerUnlisten() if @_workerUnlisten
render: =>
- items = [].concat(@_renderSyncActivityItem(), @_renderNotificationActivityItems(), @_renderTaskActivityItems())
+ items = [@_renderNotificationActivityItems(), @_renderTaskActivityItems()]
+
+ if @state.isInitialSyncComplete
+ if @state.receivingDelta
+ items.push @_renderDeltaSyncActivityItem()
+ else
+ items.push
- if @state.receivingDelta
- items.push @_renderDeltaSyncActivityItem()
names = classNames
"sidebar-activity": true
@@ -64,39 +68,6 @@ class ActivitySidebar extends React.Component
{inside}
- _renderSyncActivityItem: =>
- count = 0
- fetched = 0
- progress = 0
- incomplete = 0
- error = null
-
- for acctId, state of @state.sync
- for model, modelState of state
- incomplete += 1 unless modelState.complete
- error ?= modelState.error
- if modelState.count
- count += modelState.count / 1
- fetched += modelState.fetched / 1
-
- progress = (fetched / count) * 100 if count > 0
-
- if incomplete is 0
- return []
- else if error
-
-
Initial sync encountered an error. Waiting to retry...
-
Try Again
-
-
- else
-
-
_renderTaskActivityItems: =>
summary = {}
@@ -131,16 +102,13 @@ class ActivitySidebar extends React.Component
- _onTryAgain: =>
- Actions.retryInitialSync()
-
_onDataChanged: =>
@setState(@_getStateFromStores())
_getStateFromStores: =>
notifications: NotificationStore.notifications()
tasks: TaskQueueStatusStore.queue()
- sync: NylasSyncStatusStore.state()
+ isInitialSyncComplete: NylasSyncStatusStore.isComplete()
_onDeltaReceived: (countDeltas) =>
tooSmallForNotification = countDeltas <= 10
diff --git a/internal_packages/notifications/lib/initial-sync-activity.cjsx b/internal_packages/notifications/lib/initial-sync-activity.cjsx
new file mode 100644
index 000000000..2d496717c
--- /dev/null
+++ b/internal_packages/notifications/lib/initial-sync-activity.cjsx
@@ -0,0 +1,109 @@
+_ = require 'underscore'
+_str = require 'underscore.string'
+classNames = require 'classnames'
+React = require 'react'
+{Actions, AccountStore, NylasSyncStatusStore} = require 'nylas-exports'
+
+class InitialSyncActivity extends React.Component
+ @displayName: 'InitialSyncActivity'
+
+ constructor: (@props) ->
+ @state = @_getStateFromStores()
+
+ componentDidMount: =>
+ @_usub = NylasSyncStatusStore.listen @_onDataChanged
+
+ componentWillUnmount: =>
+ @_usub?()
+
+ _onDataChanged: =>
+ @setState(@_getStateFromStores())
+
+ _getStateFromStores: =>
+ sync: NylasSyncStatusStore.state()
+
+ render: =>
+ count = 0
+ fetched = 0
+ totalProgress = 0
+ incomplete = 0
+ error = null
+
+ for acctId, state of @state.sync
+ for model, modelState of state
+ incomplete += 1 unless modelState.complete
+ error ?= modelState.error
+ if modelState.count
+ count += modelState.count / 1
+ fetched += modelState.fetched / 1
+
+ totalProgress = (fetched / count) * 100 if count > 0
+
+ classSet = classNames
+ 'item': true
+ 'expanded-sync': @state.expandedSync
+
+ if incomplete is 0
+ return false
+ else if error
+
+
Initial sync encountered an error. Waiting to retry...
+
Try Again
+
+ {@_expandedSyncState()}
+
+ else
+ @setState expandedSync: !@state.expandedSync}>
+ {@_renderProgressBar(totalProgress)}
+
Syncing mail data…
+ {@_expandedSyncState()}
+
+
+ _expandedSyncState: ->
+ accounts = []
+ for acctId, state of @state.sync
+ account = _.findWhere(AccountStore.items(), id: acctId)
+ continue unless account
+ modelStates = _.map state, (modelState, model) =>
+ @_renderModelProgress(model, modelState, 100)
+
+ accounts.push
+
{account.emailAddress}
+ {modelStates}
+
+ accounts.push Hide
+
+ {accounts}
+
+
+ _hideExpandedState: (event) =>
+ event.stopPropagation() # So it doesn't reach the parent's onClick
+ event.preventDefault()
+ @setState expandedSync: false
+ return
+
+ _renderModelProgress: (model, modelState) ->
+ if modelState.error
+ status = "error"
+ else if modelState.complete
+ status = "complete"
+ else
+ status = "busy"
+ percent = (+modelState.fetched / +modelState.count) * 100
+
+
+
{_str.titleize(model)}:
+ {@_renderProgressBar(percent)}
+
{_str.numberFormat(modelState.fetched)} / {_str.numberFormat(modelState.count)}
+
{modelState.error}
+
+
+ _renderProgressBar: (percent) ->
+
+
+ _onTryAgain: =>
+ Actions.retryInitialSync()
+
+module.exports = InitialSyncActivity
diff --git a/internal_packages/notifications/stylesheets/notifications.less b/internal_packages/notifications/stylesheets/notifications.less
index ac06a4917..8850e9dac 100644
--- a/internal_packages/notifications/stylesheets/notifications.less
+++ b/internal_packages/notifications/stylesheets/notifications.less
@@ -19,16 +19,17 @@
font-size: @font-size-small;
color: @text-color-subtle;
line-height:@line-height-computed * 0.95;
- height:140px;
overflow-y:scroll;
box-shadow:inset 0 1px 0 @border-color-divider;
+ &:hover { cursor: default }
.item {
border-bottom:1px solid @border-color-divider;
+ &:hover { cursor: default }
.inner {
padding: @padding-large-vertical @padding-base-horizontal @padding-large-vertical @padding-base-horizontal;
- margin-top:3px;
+ margin: 3px 0;
}
.count {
color: @text-color-very-subtle;
@@ -45,6 +46,7 @@
display:block;
height:3px;
font-size:0;
+ background: rgba(0,0,0,0.1);
.progress {
transition: width 0.4s;
height:3px;
@@ -55,6 +57,42 @@
// properly. Removing position relative causes the div to remain visible
position:relative;
opacity: 1;
+
+ .account-detail-area {
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height 0.2s;
+ }
+ &.expanded-sync {
+ .account-detail-area {
+ max-height: 1000px;
+ }
+ }
+
+ .account.inner {
+ border-top: 1px solid rgba(0,0,0,0.1)
+ }
+ .account h2 {
+ font-size: 14px;
+ margin: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-weight: 500;
+ }
+ .account h3 {
+ font-size: 14px;
+ margin: 1em 0 4px 0;
+ }
+ .acccount .amount {
+ margin-top: 2px;
+ }
+ .complete .progress-track .progress {
+ background-color: @nylas-green;
+ }
+ .close-expanded {
+ display: block;
+ margin: 0 @padding-base-horizontal @padding-large-vertical @padding-base-horizontal;
+ }
}
transition: height 0.4s;
diff --git a/src/flux/stores/nylas-sync-status-store.coffee b/src/flux/stores/nylas-sync-status-store.coffee
index 4b2d5e10d..f4cfa2f3c 100644
--- a/src/flux/stores/nylas-sync-status-store.coffee
+++ b/src/flux/stores/nylas-sync-status-store.coffee
@@ -30,6 +30,12 @@ class NylasSyncStatusStore extends NylasStore
state: =>
@_statesByAccount
+ isComplete: ->
+ for acctId, state of @_statesByAccount
+ for model, modelState of state
+ return false if not modelState.complete
+ return true
+
busy: =>
for accountId, states of @_statesByAccount
for key, state of states