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

Unified Diff: chrome/browser/resources/ntp4/page_list_view.js

Issue 8637001: [NTP4] Auto-deletion of empty apps panes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: more fixes and typos from reviewing myself Created 9 years 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/ntp4/page_list_view.js
diff --git a/chrome/browser/resources/ntp4/page_list_view.js b/chrome/browser/resources/ntp4/page_list_view.js
index a5065b12ff35217dad3cde04fba1ee714c689a1f..0fa8d640b3c7190a9b6f1046da8d173219bee4db 100644
--- a/chrome/browser/resources/ntp4/page_list_view.js
+++ b/chrome/browser/resources/ntp4/page_list_view.js
@@ -35,7 +35,7 @@ cr.define('ntp4', function() {
cardSlider: undefined,
/**
- * The frame div for cardSlider.
+ * The frame div for this.cardSlider.
* @type {!Element|undefined}
*/
sliderFrame: undefined,
@@ -144,8 +144,8 @@ cr.define('ntp4', function() {
if (this.pageSwitcherEnd)
ntp4.initializePageSwitcher(this.pageSwitcherEnd);
- this.shownPage = templateData['shown_page_type'];
- this.shownPageIndex = templateData['shown_page_index'];
+ this.shownPage = templateData.shown_page_type;
+ this.shownPageIndex = templateData.shown_page_index;
// Request data on the apps so we can fill them in.
// Note that this is kicked off asynchronously. 'getAppsCallback' will be
@@ -172,6 +172,16 @@ cr.define('ntp4', function() {
cr.ui.CardSlider.EventType.CARD_CHANGED,
this.cardChangedHandler_.bind(this));
+ // Handle the end of cards being changed (useful if animated).
+ this.pageList.addEventListener(
+ cr.ui.CardSlider.EventType.CARD_CHANGE_ENDED,
+ this.cardChangeEndedHandler_.bind(this));
+
+ // Handle tiles being removed from tile pages.
+ this.pageList.addEventListener(
+ ntp4.TilePage.EventType.TILE_REMOVED,
+ this.tileRemovedHandler_.bind(this));
+
// Ensure the slider is resized appropriately with the window
window.addEventListener('resize', this.onWindowResize_.bind(this));
@@ -189,7 +199,7 @@ cr.define('ntp4', function() {
* @param {string} title The title of the tile page.
* @param {bool} titleIsEditable If true, the title can be changed.
* @param {TilePage} opt_refNode Optional reference node to insert in front
- * of.
+ * of.
* When opt_refNode is falsey, |page| will just be appended to the end of
* the page list.
*/
@@ -197,6 +207,21 @@ cr.define('ntp4', function() {
// When opt_refNode is falsey, insertBefore acts just like appendChild.
this.pageList.insertBefore(page, opt_refNode);
+ if (!document.documentElement.classList.contains('starting-up')) {
+ // If we're inserting a card in front of the current card, add 1 to the
+ // current index to adjust for the new card in the order.
+ var index = Array.prototype.indexOf.call(this.tilePages, page);
+ if (typeof this.cardSlider.currentCard != 'undefined' &&
+ index <= this.cardSlider.currentCard) {
+ this.cardSlider.currentCard += 1;
+ }
+ if (page instanceof ntp4.AppsPage &&
+ this.shownPage == templateData.apps_page_id &&
+ this.getAppsPageIndex(page) <= this.shownPageIndex) {
+ this.shownPageIndex += 1;
+ }
+ }
+
// Remember special MostVisitedPage.
if (typeof ntp4.MostVisitedPage != 'undefined' &&
page instanceof ntp4.MostVisitedPage) {
@@ -227,15 +252,31 @@ cr.define('ntp4', function() {
* the app.
* @param {boolean} isUninstall True if the app is being uninstalled;
* false if the app is being disabled.
+ * @param {boolean} fromPage If the removal was from the current page.
*/
- appRemoved: function(appData, isUninstall) {
+ appRemoved: function(appData, isUninstall, fromPage) {
var app = $(appData.id);
assert(app, 'trying to remove an app that doesn\'t exist');
- if (!isUninstall)
+ if (!isUninstall) {
app.replaceAppData(appData);
- else
- app.remove();
+ } else {
+ // Unset the ID immediately, because the app is already gone. But leave
+ // the tile on the page as it animates out.
+ app.id = '';
Evan Stade 2011/12/08 02:34:56 i think this can go inside the if (fromPage) case?
Dan Beam 2011/12/08 04:46:18 Done.
+ // If the uninstall was from this page, run the blipout animation and
+ // the tile will be removed in TilePage#onContentsAnimationEnd_.
+ // Otherwise delete the tile without auto-deleting the page to avoid
+ // re-deleting the same page (or the page that slid in to take its
+ // place).
+ if (fromPage) {
+ app.classList.add('removing-tile-contents');
+ } else {
+ var tilePage = app.tile.tilePage;
+ tilePage.removeTile(app.tile, false, true);
+ this.removeAppsPageIfEmpty_(tilePage, false, true);
+ }
+ }
},
/**
@@ -257,19 +298,16 @@ cr.define('ntp4', function() {
// uninstall.
while (this.appsPages.length > 0) {
var page = this.appsPages[0];
- var dot = page.navigationDot;
this.eventTracker.remove(page);
- page.tearDown();
- page.parentNode.removeChild(page);
- dot.parentNode.removeChild(dot);
+ this.removeTilePageAndDot_(page);
}
// Get the array of apps and add any special synthesized entries
var apps = data.apps;
// Get a list of page names
- var pageNames = data.appPageNames;
+ var pageNames = data.appsPageNames;
function stringListIsEmpty(list) {
for (var i = 0; i < list.length; i++) {
@@ -338,15 +376,7 @@ cr.define('ntp4', function() {
}
var pageIndex = appData.page_index || 0;
-
- if (pageIndex >= this.appsPages.length) {
- while (pageIndex >= this.appsPages.length) {
- this.appendTilePage(new ntp4.AppsPage(),
- localStrings.getString('appDefaultPageName'),
- true);
- }
- this.updateSliderCards();
- }
+ this.ensureAppsPageAtIndex_(pageIndex);
var page = this.appsPages[pageIndex];
var app = $(appData.id);
@@ -362,15 +392,19 @@ cr.define('ntp4', function() {
* applications.
*/
appsPrefChangedCallback: function(data) {
- for (var i = 0; i < data.apps.length; ++i) {
+ for (var i = 0; i < data.apps.length; ++i)
$(data.apps[i].id).appData = data.apps[i];
- }
- // Set the App dot names. Skip the first dot (Most Visited).
+ // Set the App dot names. Skip dots that don't belong to apps pages.
var dots = this.dotList.getElementsByClassName('dot');
- var start = this.mostVisitedPage ? 1 : 0;
- for (var i = start; i < dots.length; ++i) {
- dots[i].displayTitle = data.appPageNames[i - start] || '';
+ var firstAppDot = this.getTilePageIndex(this.appsPages[0]);
+ for (var i = 0; i < data.appsPageNames.length; ++i) {
+ // When we drop an app on a navigation dot or new page we first move the
+ // tile (which calls this), *then* save the page name so sometimes we'll
+ // be one short here.
+ var dot = dots[firstAppDot + i];
+ if (dot)
+ dot.displayTitle = data.appsPageNames[i];
}
},
@@ -379,21 +413,36 @@ cr.define('ntp4', function() {
* the Slider knows about the new elements.
*/
updateSliderCards: function() {
- var pageNo = Math.min(this.cardSlider.currentCard,
- this.tilePages.length - 1);
- this.cardSlider.setCards(Array.prototype.slice.call(this.tilePages),
- pageNo);
+ var index = -1;
+ var prevPage = this.shownPage;
+ var prevIndex = this.shownPageIndex;
+ var tiles = Array.prototype.slice.call(this.tilePages);
+ // It doesn't hurt to clamp value this every time, even if it's only
+ // applicable to apps. It helps self-heal strange / un-expected data, i.e.
Evan Stade 2011/12/08 02:34:56 strange/unexepected data most likely comes from pr
Dan Beam 2011/12/08 04:46:18 Done. (changed comment)
+ // going from an apps page to the most visited page directly without
+ // setting this.shownPageIndex = 0 somehow.
+ this.shownPageIndex = Math.max(0, Math.min(this.shownPageIndex,
+ this.appsPages.length - 1));
switch (this.shownPage) {
- case templateData['apps_page_id']:
- this.cardSlider.selectCardByValue(
- this.appsPages[Math.min(this.shownPageIndex,
- this.appsPages.length - 1)]);
+ case templateData.apps_page_id:
+ index = tiles.indexOf(this.appsPages[this.shownPageIndex]);
break;
- case templateData['most_visited_page_id']:
- if (this.mostVisitedPage)
- this.cardSlider.selectCardByValue(this.mostVisitedPage);
+ case templateData.most_visited_page_id:
+ index = tiles.indexOf(this.mostVisitedPage);
break;
}
+ // If shownPage was saved as a page that's now disabled or the shownPage
+ // doesn't exist any more, index will be -1. Change to the preferred
+ // default page (first apps pane) in this case.
+ if (index < 0) {
+ this.shownPage = templateData.apps_page_id;
+ index = tiles.indexOf(this.appsPages[0]);
+ }
+ // Set the new cards and index.
+ this.cardSlider.setCards(tiles, index);
+ // If we got new page or index, update the pref.
+ if (prevIndex != this.shownPageIndex || prevPage != this.shownPage)
Evan Stade 2011/12/08 02:34:56 you should just always do this. this.shownPageInde
Dan Beam 2011/12/08 04:46:18 Done. OK, I just noticed it was often doing this
+ chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]);
},
/**
@@ -403,13 +452,10 @@ cr.define('ntp4', function() {
enterRearrangeMode: function() {
var tempPage = new ntp4.AppsPage();
tempPage.classList.add('temporary');
- this.appendTilePage(tempPage,
- localStrings.getString('appDefaultPageName'),
- true);
- var tempIndex = Array.prototype.indexOf.call(this.tilePages, tempPage);
- if (this.cardSlider.currentCard >= tempIndex)
- this.cardSlider.currentCard += 1;
+ var pageName = localStrings.getString('appDefaultPageName');
+ this.appendTilePage(tempPage, pageName, true);
this.updateSliderCards();
+ this.updatePageSwitchers();
if (ntp4.getCurrentlyDraggingTile().firstChild.canBeRemoved())
$('footer').classList.add('showing-trash-mode');
@@ -420,18 +466,11 @@ cr.define('ntp4', function() {
*/
leaveRearrangeMode: function() {
var tempPage = document.querySelector('.tile-page.temporary');
- var dot = tempPage.navigationDot;
- if (!tempPage.tileCount && tempPage != this.cardSlider.currentCardValue) {
- dot.animateRemove();
- var tempIndex = Array.prototype.indexOf.call(this.tilePages, tempPage);
- if (this.cardSlider.currentCard > tempIndex)
- this.cardSlider.currentCard -= 1;
- tempPage.parentNode.removeChild(tempPage);
- this.updateSliderCards();
- } else {
+ // Either remove a temp page if it's empty or save the page name (as an
+ // app has just been dropped on it or created somehow.
Evan Stade 2011/12/08 02:34:56 close paren
Dan Beam 2011/12/08 04:46:18 Done. http://xkcd.com/859/
+ if (tempPage && !this.removeAppsPageIfEmpty_(tempPage, true, true)) {
+ this.saveAppsPageName(tempPage, tempPage.navigationDot.displayTitle);
tempPage.classList.remove('temporary');
- this.saveAppPageName(tempPage,
- localStrings.getString('appDefaultPageName'));
}
$('footer').classList.remove('showing-trash-mode');
@@ -488,9 +527,10 @@ cr.define('ntp4', function() {
},
/**
- * Returns the index of the given page.
- * @param {AppsPage} page The AppsPage for we wish to find.
- * @return {number} The index of |page|, or -1 if it is not here.
+ * Returns the index of the given apps page.
+ * @param {AppsPage} page The AppsPage we wish to find.
+ * @return {number} The index of |page| or -1 if it is not in the
+ * collection.
*/
getAppsPageIndex: function(page) {
return Array.prototype.indexOf.call(this.appsPages, page);
@@ -507,16 +547,19 @@ cr.define('ntp4', function() {
// Don't change shownPage until startup is done (and page changes actually
// reflect user actions).
if (!document.documentElement.classList.contains('starting-up')) {
+ var prevPage = this.shownPage;
+ var prevIndex = this.shownPageIndex;
if (page.classList.contains('apps-page')) {
- this.shownPage = templateData['apps_page_id'];
+ this.shownPage = templateData.apps_page_id;
this.shownPageIndex = this.getAppsPageIndex(page);
} else if (page.classList.contains('most-visited-page')) {
- this.shownPage = templateData['most_visited_page_id'];
+ this.shownPage = templateData.most_visited_page_id;
this.shownPageIndex = 0;
} else {
console.error('unknown page selected');
}
- chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]);
+ if (prevPage != this.shownPage || prevIndex != this.shownPageIndex)
Dan Beam 2011/12/08 04:46:18 estade: I'm assuming you'll want me to remove the
+ chrome.send('pageSelected', [this.shownPage, this.shownPageIndex]);
}
// Update the active dot
@@ -527,16 +570,78 @@ cr.define('ntp4', function() {
this.updatePageSwitchers();
},
- /*
- * Save the name of an app page.
- * Store the app page name into the preferences store.
- * @param {AppsPage} appPage The app page for which we wish to save.
+ /**
+ * Save the name of an apps page.
+ * Store the apps page name into the preferences store.
+ * @param {AppsPage} appsPage The app page for which we wish to save.
* @param {string} name The name of the page.
*/
- saveAppPageName: function(appPage, name) {
- var index = this.getAppsPageIndex(appPage);
+ saveAppsPageName: function(appsPage, name) {
+ var index = this.getAppsPageIndex(appsPage);
assert(index != -1);
- chrome.send('saveAppPageName', [name, index]);
+ chrome.send('saveAppsPageName', [name, index]);
+ },
+
+ /**
+ * An Array of callbacks to be called on the next CARD_CHANGE_ENDED event
+ * handled from the cardSlider.
+ * @private
+ */
+ cardChangeEndedCallbacks_: [],
+
+ /**
+ * Handler for CARD_CHANGE_ENDED on cardSlider.
+ * @param {Event} e The CARD_CHANGE_ENDED event.
+ * @private
+ */
+ cardChangeEndedHandler_: function(e) {
+ if (!document.documentElement.classList.contains('starting-up')) {
+ while (this.cardChangeEndedCallbacks_.length > 0)
+ this.cardChangeEndedCallbacks_.shift().call(this, e);
+ }
+ },
+
+ /**
+ * Happens when a tile is removed from a tile page.
+ * @param {Event} e An event dispatched from a tile when it is removed.
+ */
+ tileRemovedHandler_: function(e) {
+ if (e.tilePage instanceof ntp4.AppsPage)
+ this.removeAppsPageIfEmpty_(e.tilePage, e.wasAnimated);
+ },
+
+ /**
+ * Remove an apps page if it now has no tiles (is empty).
+ * @param {AppsPage} appsPage A page to check for emptiness.
+ * @param {boolean=} opt_animate Whether the prospective removal should be
+ * animated.
+ * @param {boolean=} opt_dontNotify Whether this NTP's AppLauncherHandler
+ * shouldn't be notified of this pages' prospective removal (default is
+ * to notify).
+ * @return {boolean} If |appsPage| was removed or not.
+ */
+ removeAppsPageIfEmpty_: function(appsPage, opt_animate, opt_dontNotify) {
+ assert(appsPage instanceof ntp4.AppsPage,
+ '|appsPage| is really an AppsPage');
Evan Stade 2011/12/08 02:34:56 isn't the string an error message? Hence you would
Dan Beam 2011/12/08 04:46:18 Done.
+ if (appsPage.tileCount == 0) {
+ var whenDone = function() {
Evan Stade 2011/12/08 02:34:56 some comment about whenDone is necessary
Dan Beam 2011/12/08 04:46:18 Done.
+ if (!opt_dontNotify) {
Evan Stade 2011/12/08 02:34:56 no curlies
Dan Beam 2011/12/08 04:46:18 Done.
+ chrome.send('deleteAppsPage', [this.getAppsPageIndex(appsPage)]);
+ }
+ this.removeTilePageAndDot_(appsPage, opt_animate);
+ this.updateSliderCards();
+ this.updatePageSwitchers();
+ };
Evan Stade 2011/12/08 02:34:56 newline
Dan Beam 2011/12/08 04:46:18 Done.
+ // If currently on the empty apps pane, move off of it before deleting.
+ if (appsPage === this.cardSlider.currentCardValue) {
+ this.cardChangeEndedCallbacks_.push(whenDone);
+ this.moveOffEmptyAppsPage(appsPage, opt_animate);
+ } else {
+ whenDone.call(this);
+ }
+ return true;
+ }
+ return false;
Evan Stade 2011/12/08 02:34:56 if (appsPage.tileCount != 0) return false; ...
Dan Beam 2011/12/08 04:46:18 Done.
},
/**
@@ -587,7 +692,109 @@ cr.define('ntp4', function() {
this.cardSlider.selectCard(cardIndex, true);
e.stopPropagation();
- }
+ },
+
+ /**
+ * Re-order apps on this inactive page when an active NTP gets re-ordered.
+ * @param {Object} data Position hashmap ordered by app id with indices:
+ * {id => {page_index: ##, app_launch_index: ##}}
+ */
+ appsReordered: function(data) {
+ var pages = Object.keys(data);
+ for (var i = 0; i < pages.length; ++i) {
+ var page = data[pages[i]];
+ var indices = Object.keys(page);
+ for (var j = 0; j < indices.length; ++j) {
+ var app = $(page[indices[j]]);
+ var originalPage = app.tile.tilePage;
+ if (i != this.getAppsPageIndex(originalPage) || j != app.tile.index) {
+ originalPage.removeTile(app.tile, false, true);
+ this.ensureAppsPageAtIndex_(i);
+ this.appsPages[i].addTileAt(app, j);
+ }
+ }
+ }
+ for (var i = 0; i < this.appsPages.length; ++i) {
+ // If a page is now empty, remove it and move backward one in our
Evan Stade 2011/12/08 02:34:56 comments are nice but this code is self-documentin
Dan Beam 2011/12/08 04:46:18 Done.
+ // array-like object as it is a live NodeList and the length/indice
Evan Stade 2011/12/08 02:34:56 INDICE
Dan Beam 2011/12/08 04:46:18 DONE. (well, removed whole comment...)
+ // change when you update it. Otherwise, make sure the tiles are in the
+ // right positions (just like after a user is done dragging).
+ if (this.removeAppsPageIfEmpty_(this.appsPages[i], false, true))
+ --i;
+ else
+ this.appsPages[i].cleanupDrag();
+ }
+ },
+
+ /**
+ * Ensure there is an apps page at the given |index|. If index is smaller
Evan Stade 2011/12/08 02:34:56 smaller??
Dan Beam 2011/12/08 04:46:18 Done.
+ * than the current number of apps pages, additional apps pages will be
+ * created.
+ * @param {number} index An apps page index to ensure exists.
+ */
+ ensureAppsPageAtIndex_: function(index) {
+ if (index >= this.appsPages.length) {
+ var pageName = localStrings.getString('appDefaultPageName');
+ while (index >= this.appsPages.length)
+ this.appendTilePage(new ntp4.AppsPage(), pageName, true);
+ this.updateSliderCards();
+ }
+ },
+
+ /**
+ * Returns the index of a given tile page.
+ * @param {TilePage} page The TilePage we wish to find.
+ * @return {number} The index of |page| or -1 if it is not in the
+ * collection.
+ */
+ getTilePageIndex: function(page) {
+ return Array.prototype.indexOf.call(this.tilePages, page);
+ },
+
+ /**
+ * Move to a non-empty page before/while we delete an empty one.
+ * @param {AppsPage} appsPage An empty apps page that we want to move off of
+ * (probably because it's empty).
+ * @param {boolean=} opt_animate Whether we should animate or not.
+ */
+ moveOffEmptyAppsPage: function(appsPage, opt_animate) {
+ assert(appsPage instanceof ntp4.AppsPage,
+ '|appsPage| is really an AppsPage');
+ // Select the page further toward the end than the empty |appsPage|. If
+ // the empty |appsPage| is at the end, the apps page before it will be
+ // selected.
+ var index = this.getAppsPageIndex(appsPage);
+ assert(index >= 0, '|appsPage| is in this.appsPages');
+ var direction = (index == this.appsPages.length - 1 ? -1 : 1);
Evan Stade 2011/12/08 02:34:56 no ()
Dan Beam 2011/12/08 04:46:18 Done. (but I still don't agree that this is easier
+ this.cardSlider.selectCard(this.cardSlider.currentCard + direction,
+ opt_animate);
+ },
+
+ /**
+ * Removes a page and navigation dot (if the navdot exists).
+ * @param {TilePage} page The page to be removed.
+ * @param {boolean=} opt_animate If the removal should be animated.
+ */
+ removeTilePageAndDot_: function(page, opt_animate) {
+ if (!document.documentElement.classList.contains('starting-up')) {
+ // If the card being deleted is before the current card, keep the same
+ // spot in the list by removing 1 from the current card index.
+ var index = Array.prototype.indexOf.call(this.tilePages, page);
+ if (index < this.cardSlider.currentCard) {
Evan Stade 2011/12/08 02:34:56 no curlies
Dan Beam 2011/12/08 04:46:18 Done.
+ this.cardSlider.currentCard -= 1;
+ }
+ // If the page we're about to delete is in front of the current page,
+ // remove 1 from the shown page index.
+ if (page instanceof ntp4.AppsPage &&
+ this.shownPage == templateData.apps_page_id &&
+ this.getAppsPageIndex(page) < this.shownPageIndex) {
+ this.shownPageIndex -= 1;
Evan Stade 2011/12/08 02:34:56 this doesn't seem necessary. Wont the shownPageInd
Dan Beam 2011/12/08 04:46:18 cardSlider.currentCard is an ES5 getter but all it
Dan Beam 2011/12/08 07:27:52 Working on fixing this to be in cardSlider instead
+ }
+ }
+ if (page.navigationDot)
+ page.navigationDot.remove(opt_animate);
+ page.remove();
+ },
};
return {

Powered by Google App Engine
This is Rietveld 408576698