From 6ab6c0709db4c6ff7d7cba70247af00378be9fe1 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 7 Nov 2016 18:08:16 +0100 Subject: [PATCH] Load network apps manifests as contentHash (no coding) (#3235) * Add mobx * Use mobx store for dapps * Cleanup hidden reads * Remove (now) unused hidden.js * _ denotes internal functions * s/visibleApps/visible/ * AddDapps now use the mobx store as well * Move modalOpen state to store * Simplify * Complete master merge * Remove extra indirection * Remove unneeded check * Readability improvements * Remove final debug info * Load network manifests from the network * Swallow manifest errors * introduce fetchManifest --- js/src/contracts/dappreg.js | 4 + js/src/views/Dapp/dapp.js | 11 +- js/src/views/Dapps/dappsStore.js | 206 ++++++++++++++++++------------- 3 files changed, 128 insertions(+), 93 deletions(-) diff --git a/js/src/contracts/dappreg.js b/js/src/contracts/dappreg.js index 5272f6561..ae982af56 100644 --- a/js/src/contracts/dappreg.js +++ b/js/src/contracts/dappreg.js @@ -57,4 +57,8 @@ export default class DappReg { getContent (id) { return this.meta(id, 'CONTENT'); } + + getManifest (id) { + return this.meta(id, 'MANIFEST'); + } } diff --git a/js/src/views/Dapp/dapp.js b/js/src/views/Dapp/dapp.js index 3e5b206ad..399d7b125 100644 --- a/js/src/views/Dapp/dapp.js +++ b/js/src/views/Dapp/dapp.js @@ -44,12 +44,6 @@ export default class Dapp extends Component { let src = null; switch (app.type) { - case 'builtin': - const dapphost = process.env.NODE_ENV === 'production' && !app.secure - ? `${dappsUrl}/ui` - : ''; - src = `${dapphost}/${app.url}.html`; - break; case 'local': src = `${dappsUrl}/${app.id}/`; break; @@ -57,7 +51,10 @@ export default class Dapp extends Component { src = `${dappsUrl}/${app.contentHash}/`; break; default: - console.error('unknown type', app.type); + const dapphost = process.env.NODE_ENV === 'production' && !app.secure + ? `${dappsUrl}/ui` + : ''; + src = `${dapphost}/${app.url}.html`; break; } diff --git a/js/src/views/Dapps/dappsStore.js b/js/src/views/Dapps/dappsStore.js index 6064ad91b..443a58649 100644 --- a/js/src/views/Dapps/dappsStore.js +++ b/js/src/views/Dapps/dappsStore.js @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import BigNumber from 'bignumber.js'; import { action, computed, observable } from 'mobx'; import Contracts from '../../contracts'; @@ -63,21 +62,6 @@ const builtinApps = [ } ]; -// TODO: This is just since we are moving gavcoin to its own repo, for a proper network solution -// we need to pull the network apps from the dapp registry. (Builtins & local apps unaffected) -// TODO: Manifest needs to be pulled from the content as well, however since the content may or may -// not be available locally (and refreshes work for index, and will give a 503), we are putting it -// in here. This part needs to be cleaned up. -const networkApps = [ - { - id: '0xd798a48831b4ccdbc71de206a1d6a4aa73546c7b6f59c22a47452af414dc64d6', - name: 'GAVcoin', - description: 'Manage your GAVcoins, the hottest new property in crypto', - author: 'Gavin Wood', - version: '1.0.0' - } -]; - export default class DappsStore { @observable apps = []; @observable hidden = []; @@ -118,93 +102,143 @@ export default class DappsStore { : ''; } - @action _fetch () { + _fetch () { + Promise + .all([ + this._fetchLocal(), + this._fetchRegistry() + ]) + .then(([localApps, registryApps]) => { + this.apps = [] + .concat(localApps) + .concat(registryApps) + .filter((app) => app.id) + .sort((a, b) => (a.name || '').localeCompare(b.name || '')); + }) + .catch((error) => { + console.warn('DappStore:fetch', error); + }); + } + + _fetchRegistry () { const { dappReg } = Contracts.get(); + return dappReg + .count() + .then((_count) => { + const count = _count.toNumber(); + const promises = []; + + for (let index = 0; index < count; index++) { + promises.push(dappReg.at(index)); + } + + return Promise.all(promises); + }) + .then((appsInfo) => { + const appIds = appsInfo.map(([appId, owner]) => { + return this._api.util.bytesToHex(appId); + }); + + return Promise + .all([ + Promise.all(appIds.map((appId) => dappReg.getImage(appId))), + Promise.all(appIds.map((appId) => dappReg.getContent(appId))), + Promise.all(appIds.map((appId) => dappReg.getManifest(appId))) + ]) + .then(([imageIds, contentIds, manifestIds]) => { + return appIds.map((appId, index) => { + const app = builtinApps.find((ba) => ba.id === appId) || { + id: appId, + contentHash: this._api.util.bytesToHex(contentIds[index]).substr(2), + manifestHash: this._api.util.bytesToHex(manifestIds[index]).substr(2), + type: 'network' + }; + + app.image = hashToImageUrl(imageIds[index]); + app.type = app.type || 'builtin'; + + return app; + }); + }); + }) + .then((apps) => { + return Promise + .all(apps.map((app) => { + return app.manifestHash + ? this._fetchManifest(app.manifestHash) + : null; + })) + .then((manifests) => { + return apps.map((app, index) => { + const manifest = manifests[index]; + + if (manifest) { + app.manifestHash = null; + Object.keys(manifest) + .filter((key) => key !== 'id') + .forEach((key) => { + app[key] = manifest[key]; + }); + } + + return app; + }); + }) + .then((apps) => { + return apps.filter((app) => { + return !app.manifestHash && app.id; + }); + }); + }) + .catch((error) => { + console.warn('DappsStore:fetchRegistry', error); + }); + } + + _fetchManifest (manifestHash, count = 0) { + return fetch(`${this._getHost()}/api/content/${manifestHash}/`) + .then((response) => { + if (response.ok) { + return response.json(); + } + + if (count < 1) { + return this._fetchManifest(manifestHash, count + 1); + } + + return null; + }) + .catch(() => { + if (count < 1) { + return this._fetchManifest(manifestHash, count + 1); + } + + return null; + }); + } + + _fetchLocal () { return fetch(`${this._getHost()}/api/apps`) .then((response) => { return response.ok ? response.json() : []; }) - .catch((error) => { - console.warn('DappsStore:fetch', error); - return []; - }) - .then((_localApps) => { - const localApps = _localApps - .filter((app) => !['ui'].includes(app.id)) + .then((localApps) => { + return localApps + .filter((app) => app && app.id && !['ui'].includes(app.id)) .map((app) => { app.type = 'local'; return app; }); - - return this._api.parity - .registryAddress() - .then((registryAddress) => { - if (new BigNumber(registryAddress).eq(0)) { - return []; - } - - const _builtinApps = builtinApps - .map((app) => { - app.type = 'builtin'; - return app; - }); - - return networkApps - .map((app) => { - app.type = 'network'; - return app; - }) - .concat(_builtinApps); - }) - .then((registryApps) => { - return registryApps - .concat(localApps) - .sort((a, b) => (a.name || '').localeCompare(b.name || '')); - }); - }) - .then((apps) => { - return Promise - .all([ - Promise.all(apps.map((app) => dappReg.getImage(app.id))), - Promise.all(apps.map((app) => dappReg.getContent(app.id))) - ]) - .then(([images, content]) => { - this.apps = apps.map((app, index) => { - return Object.assign(app, { - image: hashToImageUrl(images[index]), - contentHash: this._api.util.bytesToHex(content[index]).substr(2) - }); - }); - }); }) .catch((error) => { - console.warn('DappsStore:fetch', error); + console.warn('DappsStore:fetchLocal', error); }); } - _manifest (app, contentHash) { - fetch(`${this.getHost()}/${contentHash}/manifest.json`) - .then((response) => { - return response.ok - ? response.json() - : {}; - }) - .then((manifest) => { - Object.keys.forEach((key) => { - app[key] = manifest[key]; - }); - - return app; - }) - .catch((error) => { - console.warn('DappsStore:manifest', error); - }); - } - - @action _readHiddenApps () { + _readHiddenApps () { const stored = localStorage.getItem('hiddenApps'); if (stored) {