From 9bb159afe7b4f506a7abebb7090ee8980bc6a31d Mon Sep 17 00:00:00 2001 From: Amir Nissim Date: Thu, 17 Jul 2014 17:24:53 +0300 Subject: [PATCH] Bug 1038578 - [Collection App] Icons are not updated after installing hosted apps from marketplace --- apps/collection/js/add_to_collection.js | 46 +------- apps/collection/js/collection_icon.js | 9 -- apps/collection/js/common.js | 51 +++++++- apps/collection/js/create_collection.js | 6 +- apps/collection/js/native_info.js | 2 +- apps/collection/js/objects.js | 110 +++++++++++++----- apps/collection/js/synchronize.js | 5 +- apps/collection/synchronize.html | 4 +- .../test/unit/view_collection_test.js | 1 + 9 files changed, 143 insertions(+), 91 deletions(-) diff --git a/apps/collection/js/add_to_collection.js b/apps/collection/js/add_to_collection.js index 71eba386c6b5..430cba541ca5 100644 --- a/apps/collection/js/add_to_collection.js +++ b/apps/collection/js/add_to_collection.js @@ -3,10 +3,8 @@ /* global BaseCollection */ /* global CollectionIcon */ /* global CollectionsDatabase */ -/* global Common */ /* global eme */ /* global HomeIcons */ -/* global PinnedHomeIcon */ (function(exports) { @@ -32,49 +30,13 @@ CollectionsDatabase.get(event.data.collectionId).then(fresh => { var collection = BaseCollection.create(fresh); - var newPinned = new PinnedHomeIcon(event.data.identifier); - HomeIcons.init().then(() => { - - // If a record is already pinned, delete it so it appears first. - for (var i = 0, iLen = collection.pinned.length; i < iLen; i++) { - if (collection.pinned[i].identifier === newPinned.identifier) { - collection.pinned.splice(i, 1); - break; - } - } - - // If we don't have webicons, then we have likely never fetched this - //collection. Make a call to the server to fetch the apps. - if (!collection.webicons.length) { - var options = collection.categoryId ? - {categoryId: collection.categoryId} : - {query: collection.query}; - - eme.init() - .then(() => eme.api.Apps.search(options)) - .then((response) => { - collection.addWebResults(response.response.apps); - }) - .then(() => { - Common.getBackground(collection, grid.maxIconSize) - .then((bgObject) => { - collection.background = bgObject; - this.pinAndSave(newPinned, collection); - }); - }); - } else { - this.pinAndSave(newPinned, collection); - } + eme.init() + .then(() => HomeIcons.init()) + .then(() => { + collection.dropHomeIcon(event.data.identifier); }); }); - }, - - pinAndSave: function(newPinned, collection) { - collection.pinned.unshift(newPinned); - collection.renderIcon().then(() => { - collection.save(); - }); } }; diff --git a/apps/collection/js/collection_icon.js b/apps/collection/js/collection_icon.js index 0cde0c09296b..62302fe45259 100644 --- a/apps/collection/js/collection_icon.js +++ b/apps/collection/js/collection_icon.js @@ -31,9 +31,6 @@ /* constants */ - // number of app icons in the collection icon - const numAppIcons = 3; - // darkness of to side icons const sideIconDarken = 0.65; @@ -102,12 +99,6 @@ this.bgSrc = config.bgSrc || null; } - Object.defineProperty(CollectionIcon, 'numAppIcons', { - get: function get() { - return numAppIcons; - } - }); - CollectionIcon.init = function init(maxIconSize) { // measurements are based on a 60px icon (cf. bug 965711) scale = maxIconSize/60; diff --git a/apps/collection/js/common.js b/apps/collection/js/common.js index 75cbf7afd548..b1c26076bae7 100644 --- a/apps/collection/js/common.js +++ b/apps/collection/js/common.js @@ -20,11 +20,16 @@ const mozBgUrl = 'http://fxos.cdn.mozilla.net/collection/background/{categoryId}' + suffix; + const APPS_IN_ICON = 3; function Common() {} Common.prototype = { + APPS_IN_ICON: APPS_IN_ICON, + + chooseBackgroundRatio: chooseBackgroundRatio, + b64toBlob: function b64toBlob(b64) { return new Promise((resolve, reject) => { var img = new Image(); @@ -46,8 +51,6 @@ }); }, - chooseBackgroundRatio: chooseBackgroundRatio, - // TODO // add support for 'size' parameter (like getEmeBackground has) to fetch // smaller backgrounds when the full size image is not required @@ -59,7 +62,7 @@ var url = mozBgUrl.replace('{categoryId}', collection.categoryId); var xhr = new XMLHttpRequest({mozSystem: true}); - xhr.open('GET', url); + xhr.open('GET', url, true); xhr.responseType = 'blob'; return new Promise((resolve, reject) => { @@ -153,6 +156,48 @@ .catch(function (e) { eme.log('getBackground', 'failed', e); }); + }, + + getWebIcons: function getWebIcons(collection) { + var options = + (collection.categoryId) ? {categoryId: collection.categoryId} : + {query: collection.query}; + + options.limit = APPS_IN_ICON; + + return eme.api.Apps.search(options).then(response => { + var webicons = + response.response.apps.slice(0, APPS_IN_ICON).map(app => app.icon); + + return webicons; + }); + }, + + /** + * prepares the assets needed for rendering a collection's icon: + * 1. background + * 2. web icons + * + * returns a promise resolved when all assets requests are done and + * updates the collection instance but not the db + * + */ + prepareAssets: function prepareAssets(collection) { + var ready = Promise.resolve(null); + + var backgroundPromise = collection.backgroundReady ? + ready : this.getBackground(collection); + + var iconsPromise = collection.iconsReady ? + ready : this.getWebIcons(collection); + + return Promise.all([iconsPromise, backgroundPromise]) + .then((results) => { + // results are null if not fetched + collection.webicons = results[0] || collection.webicons; + collection.background = results[1] || collection.background; + return collection; + }); } }; diff --git a/apps/collection/js/create_collection.js b/apps/collection/js/create_collection.js index e561e3c5cbcf..b21acf5ad228 100644 --- a/apps/collection/js/create_collection.js +++ b/apps/collection/js/create_collection.js @@ -32,7 +32,7 @@ var maxIconSize = activity.source.data.maxIconSize; CollectionIcon.init(maxIconSize); - var numAppIcons = CollectionIcon.numAppIcons; + const APPS_IN_ICON = Common.APPS_IN_ICON; cancel.addEventListener('click', function() { // TODO request should always have an 'abort' method @@ -75,10 +75,10 @@ // collection from custom query // we make another request to get web app icons dataReady = new Promise(function getIcons(resolve) { - eme.api.Apps.search({query: selected, limit: numAppIcons}) + eme.api.Apps.search({query: selected, limit: APPS_IN_ICON}) .then(function success(response) { var webicons = - response.response.apps.slice(0,numAppIcons).map( + response.response.apps.slice(0,APPS_IN_ICON).map( function each(app) { return app.icon; }); diff --git a/apps/collection/js/native_info.js b/apps/collection/js/native_info.js index 8e958c69752a..ee19f11b4bc4 100644 --- a/apps/collection/js/native_info.js +++ b/apps/collection/js/native_info.js @@ -10,7 +10,7 @@ const SETUP_KEY = 'NativeInfo-setup'; function onerror(e) { - eme.error('NativeInfo error', e.name || e.message || e); + eme.error('NativeInfo error', e && (e.name || e.message || e)); } // Provides information about native apps in order to match them against diff --git a/apps/collection/js/objects.js b/apps/collection/js/objects.js index f7236a6d6e63..8415f0eda610 100644 --- a/apps/collection/js/objects.js +++ b/apps/collection/js/objects.js @@ -2,12 +2,14 @@ /* global eme */ /* global CollectionsDatabase */ /* global CollectionIcon */ +/* global Common */ /* global GaiaGrid */ /* global HomeIcons */ /* global SearchDedupe */ /* global GridIconRenderer */ (function(exports) { + const APPS_IN_ICON = Common.APPS_IN_ICON; // web result created from E.me API data function WebResult(data, gridItemFeatures) { @@ -53,7 +55,10 @@ // save copy of original properties so we can tell when to re-render the // collection icon - this.originalProps = props; + this.originalProps = { + pinned: props.pinned ? props.pinned.slice() : [], + background: props.background || {} + }; if (window.SearchDedupe) { this.dedupe = new SearchDedupe(); @@ -71,6 +76,17 @@ BaseCollection.prototype = { + // let's us know if we have enough app icons for rendering the + // collection's icon + get iconsReady() { + return this.pinned.length + this.webicons.length >= APPS_IN_ICON; + }, + + // let's us know if the background is ready for rendering + get backgroundReady() { + return this.background && this.background.blob; + }, + get localizedName() { // l10n prefix taken from /shared/locales/collection_categories var l10nId = 'collection-categoryId-' + this.categoryId; @@ -94,7 +110,8 @@ */ save: function save(method) { if (this.iconDirty) { - return this.renderIcon().then(this.write.bind(this, method)); + return this.renderIcon() + .then(() => this.write(method), () => this.write(method)); } else { return this.write(method); } @@ -117,8 +134,14 @@ background: this.background, icon: this.icon }; + + // update instance + this.originalProps = toSave; + + // update db return CollectionsDatabase[method](toSave).then(() => { this.id = toSave.id; + eme.log(this.name, 'saved to CollectionsDatabase'); }); }, @@ -129,33 +152,36 @@ * - The background image changes. */ get iconDirty() { - var numAppIcons = CollectionIcon.numAppIcons; - var before = this.originalProps; + var original = this.originalProps; try { // background - if (before.background.blob !== this.background.blob) { - this.originalProps.background = this.background; + if ((original.background && original.background.blob) !== + (this.background && this.background.blob)) { return true; } // apps - var first = this.pinned.concat(this.webResults).slice(0, numAppIcons); - var oldFirst = - before.pinned.concat(before.webResults).slice(0, numAppIcons); + var first = this.pinned.concat(this.webResults).slice(0, APPS_IN_ICON); + var oFirst = + original.pinned.concat(original.webResults).slice(0, APPS_IN_ICON); - for (var i = 0; i < numAppIcons; i++) { - if (first[i].identifier !== oldFirst[i].identifier) { - before.pinned = this.pinned; + if (first.length !== oFirst.length) { + return true; + } + + for (var i = 0; i < APPS_IN_ICON; i++) { + var item = first[i]; + var oItem = oFirst[i]; + + if ((item && item.identifier) !== (oItem && oItem.identifier)) { return true; } } - if (first.length !== before.length) { - before.pinned = this.pinned; - return true; - } + } catch (e) { + eme.error('icon dirty checking failed', e); + } - } catch (e) {} return false; }, @@ -174,8 +200,9 @@ if (newItems.length) { this.pinned = this.pinned.concat(newItems); - this.save(); - eme.log(newItems.length, 'new pinned to', this.name); + this.save() + .then(() => eme.log(newItems.length, 'new pinned to', this.name)); + } }, @@ -186,6 +213,21 @@ this.pin(items); }, + dropHomeIcon: function dropHomeIcon(identifier) { + var newPinned = new PinnedHomeIcon(identifier); + + // If a record is already pinned, delete it so it appears first. + for (var i = 0, iLen = this.pinned.length; i < iLen; i++) { + if (this.pinned[i].identifier === identifier) { + this.pinned.splice(i, 1); + break; + } + } + + this.pinned.unshift(newPinned); + this.save().then(() => eme.log(identifier, 'dropped into', this.name)); + }, + pinWebResult: function pinWebResult(data) { this.pin(new WebResult(data)); }, @@ -208,7 +250,7 @@ }); this.webResults = results; - this.webicons = arrayOfData.slice(0, CollectionIcon.numAppIcons) + this.webicons = arrayOfData.slice(0, APPS_IN_ICON) .map(app => app.icon); }, @@ -337,34 +379,42 @@ }, renderIcon: function renderIcon() { + eme.log('rendering icon for', this.name); + return Common.prepareAssets(this) + .then(() => this.doRenderIcon()) + .catch(() => this.doRenderIcon()); + }, + + doRenderIcon: function doRenderIcon() { // Build the small icons from pinned, then webicons - var numAppIcons = CollectionIcon.numAppIcons; - var iconSrcs = this.pinned.slice(0, numAppIcons) + var iconSrcs = this.pinned.slice(0, APPS_IN_ICON) .map((item) => this.toGridObject(item).icon); - if (iconSrcs.length < numAppIcons) { + if (iconSrcs.length < APPS_IN_ICON) { var moreIcons = this.webicons // bug 1028674: deupde .filter((webicon) => iconSrcs.indexOf(webicon) === -1) - .slice(0, numAppIcons - iconSrcs.length); + .slice(0, APPS_IN_ICON - iconSrcs.length); iconSrcs = iconSrcs.concat(moreIcons); } + var bgSrc = (this.background && this.background.blob) ? + URL.createObjectURL(this.background.blob) : + null; + var icon = new CollectionIcon({ iconSrcs: iconSrcs, - bgSrc: this.background ? URL.createObjectURL(this.background.blob) - : null + bgSrc: bgSrc }); - // return a promise - return icon.render().then(function success(canvas) { + return icon.render().then((canvas) => { this.icon = canvas.toDataURL(); - return this.icon; - }.bind(this)); + }); } + }; diff --git a/apps/collection/js/synchronize.js b/apps/collection/js/synchronize.js index c4ed1d7e0461..e1db39078af1 100644 --- a/apps/collection/js/synchronize.js +++ b/apps/collection/js/synchronize.js @@ -1,4 +1,5 @@ 'use strict'; +/* global eme */ /* global CollectionIcon */ /* global NativeInfo */ @@ -30,7 +31,7 @@ */ install: function(event) { var message = event.data; - NativeInfo.processApp('install', message.id); + eme.init().then(() => NativeInfo.processApp('install', message.id)); }, /** @@ -38,7 +39,7 @@ */ uninstall: function(event) { var message = event.data; - NativeInfo.processApp('uninstall', message.id); + eme.init().then(() => NativeInfo.processApp('uninstall', message.id)); } }; diff --git a/apps/collection/synchronize.html b/apps/collection/synchronize.html index b66ac4f4d88e..69e0c2a9027c 100644 --- a/apps/collection/synchronize.html +++ b/apps/collection/synchronize.html @@ -16,6 +16,7 @@ + @@ -23,8 +24,9 @@ - + + diff --git a/apps/collection/test/unit/view_collection_test.js b/apps/collection/test/unit/view_collection_test.js index 30b4bd964dd0..97a3a7bd0c03 100644 --- a/apps/collection/test/unit/view_collection_test.js +++ b/apps/collection/test/unit/view_collection_test.js @@ -4,6 +4,7 @@ /* global Promise */ /* global MocksHelper */ +require('/js/common.js'); require('/js/objects.js'); require('/test/unit/mock_contextmenu.js'); require('/test/unit/mock_view_apps.js');