Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(509)

Unified Diff: chrome/browser/resources/file_manager/js/file_manager.js

Issue 9855024: Postpone connecting to GData even more, provide progress indication. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Showing progress when opening File Manager on a GData path Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/file_manager/js/file_manager.js
diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js
index 68cde5d6460108c8f7a11a7d0425486036dd2d78..d49abbeac25c3a3ccc794aca8021d0f4f93aa06d 100644
--- a/chrome/browser/resources/file_manager/js/file_manager.js
+++ b/chrome/browser/resources/file_manager/js/file_manager.js
@@ -554,7 +554,21 @@ FileManager.prototype = {
// all paste tasks are complete.
this.pasteSuccessCallbacks_ = [];
- this.setupCurrentDirectory_();
+ var path = this.getPathFromUrlOrParams_();
+ if (path &&
+ DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) {
+ // We are opening on a GData path. Mount GData and show
+ // "Loading Google Docs" message until the directory content loads.
+ this.dialogContainer_.setAttribute('unmounted', true);
+ this.initGData_(true /* dirChanged */);
+ // Will be nulled out if a directory change happens before it fires.
+ this.setupGDataDirectoryAfterMount_ = function() {
dgozman 2012/03/28 10:28:30 Can we do the same trick as in DirectoryModel: add
Vladislav Kaznacheev 2012/03/28 14:00:57 Done.
+ this.setupGDataDirectoryAfterMount_ = null;
+ this.setupCurrentDirectory_(false /* blankWhileLoading */);
+ }.bind(this);
+ } else {
+ this.setupCurrentDirectory_(true /* blankWhileLoading */);
+ }
this.summarizeSelection_();
@@ -637,6 +651,7 @@ FileManager.prototype = {
this.spinner_ = this.dialogDom_.querySelector('.spinner');
this.showSpinner_(false);
this.butter_ = this.dialogDom_.querySelector('.butter-bar');
+ this.unmountedPanel_ = this.dialogDom_.querySelector('.unmounted-panel');
cr.ui.Table.decorate(this.table_);
cr.ui.Grid.decorate(this.grid_);
@@ -802,22 +817,89 @@ FileManager.prototype = {
this.rootsList_.dataModel = this.directoryModel_.rootsList;
this.directoryModel_.updateRoots(function() {
self.rootsList_.endBatchUpdates();
- });
+ }, false);
};
- FileManager.prototype.initGData_ = function() {
+ /**
+ * @param {boolean} dirChanged True if we just changed to GData directory,
+ * False if "Retry" button clicked.
+ */
+ FileManager.prototype.initGData_ = function(dirChanged) {
+ this.initGDataUnmountedPanel_();
+
+ this.unmountedPanel_.removeAttribute('error');
+ if (dirChanged) {
+ // When changing to GData directory we want to see a clear panel.
+ this.unmountedPanel_.removeAttribute('retry');
+ this.unmountedPanel_.removeAttribute('loading');
+ setTimeout(function() {
+ if (this.gdataLoadingTimer_) { // Still loading.
+ this.unmountedPanel_.setAttribute('loading', true);
+ }
+ }.bind(this), 500); // Avoid flicker if the mount is quick.
+ } else {
+ // When retrying we do not hide "Retry" and "Learn more".
+ this.unmountedPanel_.setAttribute('loading', true);
+ }
+
+ if (this.gdataLoadingTimer_)
dgozman 2012/03/28 10:28:30 How is this possible?
Vladislav Kaznacheev 2012/03/28 14:00:57 Added a comment: If the user changed to another di
+ return;
+
metrics.startInterval('Load.GData');
chrome.fileBrowserPrivate.addMount('', 'gdata', {});
- if (this.gdataMountTimer_) {
- clearTimeout(this.gdataMountTimer_);
+
+ this.gdataLoadingTimer_ = setTimeout(function() {
+ this.gdataLoadingTimer_ = null;
+ this.onGDataUnreachable_('GData load timeout');
+ }.bind(this),
+ 10 * 1000) ;
+ };
+
+ FileManager.prototype.clearGDataLoadingTimer_ = function(message) {
+ if (this.gdataLoadingTimer_) {
+ clearTimeout(this.gdataLoadingTimer_);
+ this.gdataLoadingTimer_ = null;
+ }
+ };
+
+ FileManager.prototype.onGDataUnreachable_ = function(message) {
+ console.warn(message);
+ if (this.isOnGData()) {
+ this.unmountedPanel_.removeAttribute('loading');
+ this.unmountedPanel_.setAttribute('error', true);
+ this.unmountedPanel_.setAttribute('retry', true);
}
- this.gdataMountTimer_ = setTimeout(function() {
- this.gdataMountTimer_ = null;
- if (this.isOnGData()) {
- // TODO(kaznacheev): show the message in the file list space.
- this.alert.show('Could not connect to GData');
- }
- }.bind(this), 10 * 1000);
+ };
+
+ FileManager.prototype.initGDataUnmountedPanel_ = function() {
+ if (this.unmountedPanel_.firstElementChild)
+ return;
+
+ var loading = this.document_.createElement('div');
+ loading.className = 'gdata loading';
+ loading.textContent = strf('GDATA_LOADING', str('GDATA_PRODUCT_NAME'));
+ this.unmountedPanel_.appendChild(loading);
+
+ var error = this.document_.createElement('div');
+ error.className = 'gdata error';
+ error.textContent = strf('GDATA_CANNOT_REACH', str('GDATA_PRODUCT_NAME'));
+ this.unmountedPanel_.appendChild(error);
+
+ var retry = this.document_.createElement('button');
+ retry.className = 'gdata retry';
+ retry.textContent = str('GDATA_RETRY');
+ retry.onclick = this.initGData_.bind(this, false /* retry */);
+ this.unmountedPanel_.appendChild(retry);
+
+ var learnMore = this.document_.createElement('div');
+ learnMore.className = 'gdata learn-more';
+ this.unmountedPanel_.appendChild(learnMore);
+
+ var learnMoreLink = this.document_.createElement('a');
+ learnMoreLink.textContent = str('GDATA_LEARN_MORE');
+ learnMoreLink.href = 'javascript://'; // TODO: Set a proper link URL.
+ learnMoreLink.className = 'gdata learn-more';
+ learnMore.appendChild(learnMoreLink);
};
/**
@@ -1518,6 +1600,12 @@ FileManager.prototype = {
errorCallback);
};
+ FileManager.prototype.getPathFromUrlOrParams_ = function() {
+ return location.hash ? // Location hash has the highest priority.
+ decodeURI(location.hash.substr(1)) :
+ this.params_.defaultPath;
+ };
+
/**
* Restores current directory and may be a selected item after page load (or
* reload) or popping a state (after click on back/forward). If location.hash
@@ -1525,84 +1613,85 @@ FileManager.prototype = {
* will be restored. defaultPath primarily is used with save/open dialogs.
* Default path may also contain a file name. Freshly opened file manager
* window has neither.
+ *
+ * @param {boolean} blankWhileLoading
*/
- FileManager.prototype.setupCurrentDirectory_ = function() {
-
- if (location.hash) {
- // Location hash has the highest priority.
- var path = decodeURI(location.hash.substr(1));
-
- // In the FULL_PAGE mode if the path points to a file we might have
- // to invoke a task after selecting it.
- if (this.dialogType_ == FileManager.DialogType.FULL_PAGE) {
- // To prevent the file list flickering for a moment before the action
- // is executed we hide it under a white div.
- var shade = this.document_.createElement('div');
+ FileManager.prototype.setupCurrentDirectory_ = function(blankWhileLoading) {
+ var path = this.getPathFromUrlOrParams_();
+
+ if (!path) {
+ this.directoryModel_.setupDefaultPath();
dgozman 2012/03/28 10:28:30 We ignore blankWhileLoading since default path is
Vladislav Kaznacheev 2012/03/28 14:00:57 It only affects the case when we (think that we) a
+ return;
+ }
+
+ // In the FULL_PAGE mode if the hash path points to a file we might have
+ // to invoke a task after selecting it.
+ // If the file path is in params_ we only want to select the file.
+ if (location.hash &&
+ this.dialogType_ == FileManager.DialogType.FULL_PAGE) {
+ // To prevent the file list flickering for a moment before the action
+ // is executed we hide it under a white div.
+ var shade;
+ if (blankWhileLoading) {
+ shade = this.document_.createElement('div');
shade.className = 'overlay-pane';
shade.style.backgroundColor = 'white';
this.document_.body.appendChild(shade);
- function removeShade() { shade.parentNode.removeChild(shade) }
-
- // Keep track of whether the path is identified as an existing leaf
- // node. Note that onResolve is guaranteed to be called (exactly once)
- // before onLoadedActivateLeaf.
- var foundLeaf = true;
- function onResolve(baseName, leafName, exists) {
- if (!exists || leafName == '') {
- // Non-existent file or a directory. Remove the shade immediately.
- removeShade();
- foundLeaf = false;
- }
+ }
+ function removeShade() {
+ if (shade)
+ shade.parentNode.removeChild(shade);
+ }
+
+ // Keep track of whether the path is identified as an existing leaf
+ // node. Note that onResolve is guaranteed to be called (exactly once)
+ // before onLoadedActivateLeaf.
+ var foundLeaf = true;
+ function onResolve(baseName, leafName, exists) {
+ if (!exists || leafName == '') {
+ // Non-existent file or a directory. Remove the shade immediately.
+ removeShade();
+ foundLeaf = false;
}
+ }
- // TODO(kaznacheev): refactor dispatchDefaultTask to accept an array
- // of urls instead of a selection. This will remove the need to wait
- // until the selection is done.
- var self = this;
- function onLoadedActivateLeaf() {
- if (foundLeaf) {
- // There are 3 ways we can get here:
- // 1. Invoked from file_manager_util::ViewFile. This can only
- // happen for 'gallery' and 'mount-archive' actions.
- // 2. Reloading a Gallery page. Must be an image or a video file.
- // 3. A user manually entered a URL pointing to a file.
- if (FileType.isImageOrVideo(path)) {
- self.dispatchInternalTask_('gallery', self.selection.urls);
- } else if (FileType.getMediaType(path) == 'archive') {
- self.dispatchInternalTask_('mount-archive', self.selection.urls);
- } else {
- // Manually entered path, do nothing, remove the shade ASAP.
- removeShade();
- return;
- }
- setTimeout(removeShade, 1000);
+ // TODO(kaznacheev): refactor dispatchDefaultTask to accept an array
+ // of urls instead of a selection. This will remove the need to wait
+ // until the selection is done.
+ var self = this;
+ function onLoadedActivateLeaf() {
+ if (foundLeaf) {
+ // There are 3 ways we can get here:
+ // 1. Invoked from file_manager_util::ViewFile. This can only
+ // happen for 'gallery' and 'mount-archive' actions.
+ // 2. Reloading a Gallery page. Must be an image or a video file.
+ // 3. A user manually entered a URL pointing to a file.
+ if (FileType.isImageOrVideo(path)) {
+ self.dispatchInternalTask_('gallery', self.selection.urls);
+ } else if (FileType.getMediaType(path) == 'archive') {
+ self.dispatchInternalTask_('mount-archive', self.selection.urls);
+ } else {
+ // Manually entered path, do nothing, remove the shade ASAP.
+ removeShade();
+ return;
}
+ setTimeout(removeShade, 1000);
}
- this.directoryModel_.setupPath(path, onLoadedActivateLeaf, onResolve);
-
- return;
}
-
- this.directoryModel_.setupPath(path);
+ this.directoryModel_.setupPath(path, onLoadedActivateLeaf, onResolve);
return;
}
- if (this.params_.defaultPath) {
- var path = this.params_.defaultPath;
- if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) {
- this.directoryModel_.setupPath(path, undefined,
- function(basePath, leafName) {
- this.filenameInput_.value = leafName;
- this.selectDefaultPathInFilenameInput_();
- }.bind(this));
- return;
- }
-
- this.directoryModel_.setupPath(path);
+ if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) {
+ this.directoryModel_.setupPath(path, undefined,
+ function(basePath, leafName) {
+ this.filenameInput_.value = leafName;
+ this.selectDefaultPathInFilenameInput_();
+ }.bind(this));
return;
}
- this.directoryModel_.setupDefaultPath();
+ this.directoryModel_.setupPath(path);
};
/**
@@ -2511,9 +2600,10 @@ FileManager.prototype = {
// We don't have tasks, so try the default browser action.
// We only do that for single selection to avoid confusion.
+ var self = this;
function callback(success) {
if (!success && selection.entries.length == 1)
- this.alert.showHtml(
+ self.alert.showHtml(
dgozman 2012/03/28 10:28:30 Callback is bound below.
Vladislav Kaznacheev 2012/03/28 14:00:57 It was not bound enough:) because the parameter to
unescape(selection.entries[0].name),
strf('NO_ACTION_FOR_FILE', NO_ACTION_FOR_FILE_URL),
function() {});
@@ -2590,13 +2680,15 @@ FileManager.prototype = {
var self = this;
var changeDirectoryTo = null;
+ var setupInitialDirectory = false;
if (event && event.mountType == 'gdata') {
metrics.recordInterval('Load.GData');
- if (this.gdataMountTimer_) {
- clearTimeout(this.gdataMountTimer_);
- this.gdataMountTimer_ = null;
- }
+ console.log("GData mounted");
+ // Not calling clearGDataLoadingTimer_ here because we want to keep
dgozman 2012/03/28 10:28:30 Is the following possible? - requesting gdata moun
Vladislav Kaznacheev 2012/03/28 14:00:57 Yes. This is intended (see http://code.google.com/
+ // "Loading Google Docs" message until the directory loads. It is OK if
+ // the timer fires after the mount because onDirectoryChanged_will hide
dgozman 2012/03/28 10:28:30 typo: onDirectoryChanged_will
Vladislav Kaznacheev 2012/03/28 14:00:57 Done.
+ // the unmounted panel.
if (event.status == 'success') {
this.gdataMounted_ = true;
this.gdataMountInfo_ = {
@@ -2605,11 +2697,16 @@ FileManager.prototype = {
"mountType": event.mountType,
"mountCondition": event.status
};
- if (this.isOnGData()) {
+ if (self.setupGDataDirectoryAfterMount_) {
+ setupInitialDirectory = true;
+ } else if (this.isOnGData() &&
+ this.directoryModel_.currentEntry.unmounted) {
// We are currently on an unmounted GData directory, force a rescan.
changeDirectoryTo = this.directoryModel_.rootPath;
}
} else {
+ this.clearGDataLoadingTimer_();
+ this.onGDataUnreachable_('GData mount failed: ' + event.status);
this.gdataMounted_ = false;
this.gdataMountInfo_ = null;
}
@@ -2664,10 +2761,13 @@ FileManager.prototype = {
// Failed mounts can "give" us new devices which might be formatted,
// so we have to refresh root list then.
self.directoryModel_.updateRoots(function() {
+ if (setupInitialDirectory && self.setupGDataDirectoryAfterMount_) {
dgozman 2012/03/28 10:28:30 I think, setupInitialDirectory is true if and only
Vladislav Kaznacheev 2012/03/28 14:00:57 I got rid of this variable completely. On 2012/03
+ self.setupGDataDirectoryAfterMount_();
+ }
if (changeDirectoryTo) {
self.directoryModel_.changeDirectory(changeDirectoryTo);
}
- });
+ }, self.gdataMounted_);
});
};
@@ -3557,7 +3657,7 @@ FileManager.prototype = {
this.watchedDirectoryUrl_ = null;
}
- if (event.newDirEntry.fullPath != '/') {
+ if (event.newDirEntry.fullPath != '/' && !event.newDirEntry.unmounted) {
this.watchedDirectoryUrl_ = event.newDirEntry.toURL();
chrome.fileBrowserPrivate.addFileWatch(this.watchedDirectoryUrl_,
function(result) {
@@ -3570,15 +3670,22 @@ FileManager.prototype = {
this.updateVolumeMetadata_();
+ if (event.newDirEntry.unmounted)
+ this.dialogContainer_.setAttribute('unmounted', true);
+ else
+ this.dialogContainer_.removeAttribute('unmounted');
+
if (this.isOnGData()) {
this.dialogContainer_.setAttribute('gdata', true);
- if (!this.requestedGDataMount_) { // Request GData mount only once.
- this.requestedGDataMount_ = true;
- this.initGData_();
+ if (event.newDirEntry.unmounted) {
+ this.initGData_(true /* directory changed */);
}
} else {
this.dialogContainer_.removeAttribute('gdata');
}
+
+ // Manual directory change cancels the GData directory setup.
+ this.setupGDataDirectoryAfterMount_ = null;
};
FileManager.prototype.updateVolumeMetadata_ = function() {

Powered by Google App Engine
This is Rietveld 408576698