From d73ff305105ffd330d389e3321e2dd3c08aca172 Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Thu, 24 Sep 2015 11:03:11 -0700 Subject: [PATCH] feat(sync): expanded details on sync status Summary: Fixes T3574 Test Plan: manual Reviewers: dillon, bengotow Reviewed By: dillon, bengotow Maniphest Tasks: T3574 Differential Revision: https://phab.nylas.com/D2058 --- .../notifications/lib/activity-sidebar.cjsx | 54 ++------- .../lib/initial-sync-activity.cjsx | 109 ++++++++++++++++++ .../stylesheets/notifications.less | 42 ++++++- .../stores/nylas-sync-status-store.coffee | 6 + 4 files changed, 166 insertions(+), 45 deletions(-) create mode 100644 internal_packages/notifications/lib/initial-sync-activity.cjsx 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 -
-
-
-
-
Syncing mail data…
-
- _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