Commit 701c967c authored by Kelvin Jiang's avatar Kelvin Jiang Committed by Commit Bot

[Extensions] Add loading state to activity log page

Add a loading state to activity log so we clear the activity log when
we switch to a new extension. This ensures that the user does not see
any previous data when new activity log data is being loaded. It also
gives users a bit more feedback on progress loading the activity log.

Bug: 832354
Change-Id: I30d5756fc375979d0a6f183af1e2e1bf25fe1073
Reviewed-on: https://chromium-review.googlesource.com/c/1345409
Commit-Queue: Kelvin Jiang <kelvinjiang@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#610165}
parent 3a4b1617
...@@ -226,6 +226,9 @@ ...@@ -226,6 +226,9 @@
<message name="IDS_MD_EXTENSIONS_LOAD_ERROR_RETRY" desc="The text on the button to retry loading an unpacked extension after a failed load."> <message name="IDS_MD_EXTENSIONS_LOAD_ERROR_RETRY" desc="The text on the button to retry loading an unpacked extension after a failed load.">
Retry Retry
</message> </message>
<message name="IDS_MD_EXTENSIONS_LOADING_ACTIVITIES" desc="The message shown to the user when the activity log for an extension is currently being fetched from the API.">
Fetching activities...
</message>
<message name="IDS_MD_EXTENSIONS_NO_ACTIVITIES" desc="The message shown to the user when an extension has no recent activities."> <message name="IDS_MD_EXTENSIONS_NO_ACTIVITIES" desc="The message shown to the user when an extension has no recent activities.">
No recent activities No recent activities
</message> </message>
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<dom-module id="extensions-activity-log"> <dom-module id="extensions-activity-log">
<template> <template>
<style include="iron-flex cr-shared-style shared-style"> <style include="iron-flex cr-shared-style shared-style">
.empty-activity-message { .activity-message {
color: #6e6e6e; color: #6e6e6e;
font-size: 123%; /* Should be 16px when 100% is 13px. */ font-size: 123%; /* Should be 16px when 100% is 13px. */
font-weight: 500; font-weight: 500;
...@@ -29,12 +29,18 @@ ...@@ -29,12 +29,18 @@
</paper-icon-button-light> </paper-icon-button-light>
<span>$i18n{activityLogPageHeading}</span> <span>$i18n{activityLogPageHeading}</span>
</div> </div>
<div id="no-activities" class="empty-activity-message" <div id="loading-activities" class="activity-message"
hidden$="[[!shouldShowLoadingMessage_(
pageState_)]]">
<span>$i18n{loadingActivities}</span>
</div>
<div id="no-activities" class="activity-message"
hidden$="[[!shouldShowEmptyActivityLogMessage_( hidden$="[[!shouldShowEmptyActivityLogMessage_(
activityData_)]]"> pageState_, activityData_)]]">
<span>$i18n{noActivities}</span> <span>$i18n{noActivities}</span>
</div> </div>
<div hidden$="[[!activityData_]]"> <div id="activity-list"
hidden$="[[!shouldShowActivities_(pageState_, activityData_)]]">
<template is="dom-repeat" items="[[activityData_.activities]]"> <template is="dom-repeat" items="[[activityData_.activities]]">
<activity-log-item id="[[item.activityId]]" data="[[item]]"> <activity-log-item id="[[item.activityId]]" data="[[item]]">
</activity-log-item> </activity-log-item>
......
...@@ -2,6 +2,20 @@ ...@@ -2,6 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
cr.exportPath('extensions');
/**
* The different states the activity log page can be in. Initial state is
* LOADING because we call the activity log API whenever a user navigates to the
* page. LOADED is the state where the API call has returned a successful
* result.
* @enum {string}
*/
const ActivityLogPageState = {
LOADING: 'loading',
LOADED: 'loaded'
};
cr.define('extensions', function() { cr.define('extensions', function() {
'use strict'; 'use strict';
...@@ -34,6 +48,15 @@ cr.define('extensions', function() { ...@@ -34,6 +48,15 @@ cr.define('extensions', function() {
*/ */
activityData_: Object, activityData_: Object,
/**
* @private
* @type {ActivityLogPageState}
*/
pageState_: {
type: String,
value: ActivityLogPageState.LOADING,
},
/** /**
* A promise resolver for any external files waiting for the * A promise resolver for any external files waiting for the
* GetExtensionActivity API call to finish. * GetExtensionActivity API call to finish.
...@@ -53,10 +76,10 @@ cr.define('extensions', function() { ...@@ -53,10 +76,10 @@ cr.define('extensions', function() {
this.getActivityLog_(); this.getActivityLog_();
// Add a listener here so we fetch the activity log whenever a user // Add a listener here so we fetch the activity log whenever a user
// navigates to the activity log from another page. // navigates to the activity log from another page. This is needed since
// This is needed since this component already exists in the background // this component already exists in the background if a user navigates
// if a user navigates away from this page so attached may not be called // away from this page so attached may not be called when a user navigates
// when a user navigates back. // back.
this.navigationListener_ = extensions.navigation.addListener(newPage => { this.navigationListener_ = extensions.navigation.addListener(newPage => {
if (newPage.page === Page.ACTIVITY_LOG) if (newPage.page === Page.ACTIVITY_LOG)
this.getActivityLog_(); this.getActivityLog_();
...@@ -74,7 +97,25 @@ cr.define('extensions', function() { ...@@ -74,7 +97,25 @@ cr.define('extensions', function() {
* @return {boolean} * @return {boolean}
*/ */
shouldShowEmptyActivityLogMessage_: function() { shouldShowEmptyActivityLogMessage_: function() {
return !this.activityData_ || this.activityData_.activities.length === 0; return this.pageState_ === ActivityLogPageState.LOADED &&
(!this.activityData_ || this.activityData_.activities.length === 0);
},
/**
* @private
* @return {boolean}
*/
shouldShowLoadingMessage_: function() {
return this.pageState_ === ActivityLogPageState.LOADING;
},
/**
* @private
* @return {boolean}
*/
shouldShowActivities_: function() {
return this.pageState_ === ActivityLogPageState.LOADED &&
!!this.activityData_ && this.activityData_.activities.length > 0;
}, },
/** @private */ /** @private */
...@@ -85,7 +126,9 @@ cr.define('extensions', function() { ...@@ -85,7 +126,9 @@ cr.define('extensions', function() {
/** @private */ /** @private */
getActivityLog_: function() { getActivityLog_: function() {
this.pageState_ = ActivityLogPageState.LOADING;
this.delegate.getExtensionActivityLog(this.extensionId).then(result => { this.delegate.getExtensionActivityLog(this.extensionId).then(result => {
this.pageState_ = ActivityLogPageState.LOADED;
this.activityData_ = result; this.activityData_ = result;
this.onDataFetched.resolve(); this.onDataFetched.resolve();
}); });
......
...@@ -227,6 +227,7 @@ content::WebUIDataSource* CreateMdExtensionsSource(bool in_dev_mode) { ...@@ -227,6 +227,7 @@ content::WebUIDataSource* CreateMdExtensionsSource(bool in_dev_mode) {
{"loadErrorFileLabel", IDS_MD_EXTENSIONS_LOAD_ERROR_FILE_LABEL}, {"loadErrorFileLabel", IDS_MD_EXTENSIONS_LOAD_ERROR_FILE_LABEL},
{"loadErrorErrorLabel", IDS_MD_EXTENSIONS_LOAD_ERROR_ERROR_LABEL}, {"loadErrorErrorLabel", IDS_MD_EXTENSIONS_LOAD_ERROR_ERROR_LABEL},
{"loadErrorRetry", IDS_MD_EXTENSIONS_LOAD_ERROR_RETRY}, {"loadErrorRetry", IDS_MD_EXTENSIONS_LOAD_ERROR_RETRY},
{"loadingActivities", IDS_MD_EXTENSIONS_LOADING_ACTIVITIES},
{"noActivities", IDS_MD_EXTENSIONS_NO_ACTIVITIES}, {"noActivities", IDS_MD_EXTENSIONS_NO_ACTIVITIES},
{"noErrorsToShow", IDS_EXTENSIONS_ERROR_NO_ERRORS_CODE_MESSAGE}, {"noErrorsToShow", IDS_EXTENSIONS_ERROR_NO_ERRORS_CODE_MESSAGE},
{"runtimeHostsDialogInputError", {"runtimeHostsDialogInputError",
......
...@@ -69,6 +69,9 @@ suite('ExtensionsActivityLogTest', function() { ...@@ -69,6 +69,9 @@ suite('ExtensionsActivityLogTest', function() {
test('activities are present for extension', function() { test('activities are present for extension', function() {
Polymer.dom.flush(); Polymer.dom.flush();
testVisible('#no-activities', false);
testVisible('#loading-activities', false);
testVisible('#activity-list', true);
expectEquals( expectEquals(
activityLog.shadowRoot.querySelectorAll('activity-log-item').length, 2); activityLog.shadowRoot.querySelectorAll('activity-log-item').length, 2);
}); });
...@@ -82,10 +85,23 @@ suite('ExtensionsActivityLogTest', function() { ...@@ -82,10 +85,23 @@ suite('ExtensionsActivityLogTest', function() {
Polymer.dom.flush(); Polymer.dom.flush();
testVisible('#no-activities', true); testVisible('#no-activities', true);
testVisible('#loading-activities', false);
testVisible('#activity-list', false);
expectEquals( expectEquals(
activityLog.shadowRoot.querySelectorAll('activity-log-item').length, 0); activityLog.shadowRoot.querySelectorAll('activity-log-item').length, 0);
}); });
test('message shown when activities are being fetched', function() {
// Pretend the activity log is still loading.
activityLog.pageState_ = ActivityLogPageState.LOADING;
Polymer.dom.flush();
testVisible('#no-activities', false);
testVisible('#loading-activities', true);
testVisible('#activity-list', false);
});
test('clicking on back button navigates to the details page', function() { test('clicking on back button navigates to the details page', function() {
Polymer.dom.flush(); Polymer.dom.flush();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment