diff --git a/js/src/contracts/dappreg.js b/js/src/contracts/dappreg.js index ef07e95c5..5272f6561 100644 --- a/js/src/contracts/dappreg.js +++ b/js/src/contracts/dappreg.js @@ -14,22 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - export default class DappReg { constructor (api, registry) { this._api = api; @@ -69,4 +53,8 @@ export default class DappReg { getImage (id) { return this.meta(id, 'IMG'); } + + getContent (id) { + return this.meta(id, 'CONTENT'); + } } diff --git a/js/src/views/Dapp/dapp.js b/js/src/views/Dapp/dapp.js index 6132b200a..3cc096f66 100644 --- a/js/src/views/Dapp/dapp.js +++ b/js/src/views/Dapp/dapp.js @@ -27,7 +27,7 @@ export default class Dapp extends Component { render () { const { name, type } = this.props.params; - const src = type === 'global' + const src = (type === 'builtin') ? `${dapphost}/${name}.html` : `http://127.0.0.1:8080/${name}/`; diff --git a/js/src/views/Dapps/Summary/summary.js b/js/src/views/Dapps/Summary/summary.js index 9cf6a046b..c06b6341c 100644 --- a/js/src/views/Dapps/Summary/summary.js +++ b/js/src/views/Dapps/Summary/summary.js @@ -38,7 +38,14 @@ export default class Summary extends Component { return null; } - const url = `/app/${app.builtin ? 'global' : 'local'}/${app.url || app.id}`; + let type = 'builtin'; + if (app.network) { + type = 'network'; + } else if (app.local) { + type = 'local'; + } + + const url = `/app/${type}/${app.url || app.contentHash || app.id}`; const image = app.image || app.iconUrl ? :
 
; diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js index a7922f7bc..4c1b7baab 100644 --- a/js/src/views/Dapps/dapps.js +++ b/js/src/views/Dapps/dapps.js @@ -22,7 +22,7 @@ import { Actionbar, Page } from '../../ui'; import FlatButton from 'material-ui/FlatButton'; import EyeIcon from 'material-ui/svg-icons/image/remove-red-eye'; -import fetchAvailable from './available'; +import { fetchAvailable } from './registry'; import { readHiddenApps, writeHiddenApps } from './hidden'; import AddDapps from './AddDapps'; @@ -42,15 +42,7 @@ export default class Dapps extends Component { } componentDidMount () { - const { api } = this.context; - - fetchAvailable(api).then((available) => { - this.setState({ - available, - hidden: readHiddenApps() - }); - this.loadImages(); - }); + this.loadAvailableApps(); } render () { @@ -84,6 +76,10 @@ export default class Dapps extends Component { } renderApp = (app) => { + if (!app.name) { + return null; + } + return (
dappReg.getImage(app.id))) - .then((images) => { - this.setState({ - available: images - .map(hashToImageUrl) - .map((image, i) => Object.assign({}, available[i], { image })) - }); - }) - .catch((error) => { - console.warn('loadImages', error); - }); - } - onHideApp = (id) => { const { hidden } = this.state; const newHidden = hidden.concat(id); @@ -134,4 +112,57 @@ export default class Dapps extends Component { closeModal = () => { this.setState({ modalOpen: false }); }; + + loadAvailableApps () { + const { api } = this.context; + + fetchAvailable(api) + .then((available) => { + this.setState({ + available, + hidden: readHiddenApps() + }); + + this.loadContent(); + }); + } + + loadContent () { + const { api } = this.context; + const { available } = this.state; + const { dappReg } = Contracts.get(); + + return Promise + .all(available.map((app) => dappReg.getImage(app.id))) + .then((images) => { + const _available = images + .map(hashToImageUrl) + .map((image, index) => Object.assign({}, available[index], { image })); + + this.setState({ available: _available }); + const _networkApps = _available.filter((app) => app.network); + + return Promise + .all(_networkApps.map((app) => dappReg.getContent(app.id))) + .then((content) => { + const networkApps = content.map((_contentHash, index) => { + const networkApp = _networkApps[index]; + const contentHash = api.util.bytesToHex(_contentHash).substr(2); + const app = _available.find((_app) => _app.id === networkApp.id); + + console.log(`found content for ${app.id} at ${contentHash}`); + return Object.assign({}, app, { contentHash }); + }); + + this.setState({ + available: _available.map((app) => { + return Object.assign({}, networkApps.find((napp) => app.id === napp.id) || app); + }) + }); + }); + }) + .catch((error) => { + console.warn('loadImages', error); + }); + } } diff --git a/js/src/views/Dapps/available.js b/js/src/views/Dapps/registry.js similarity index 53% rename from js/src/views/Dapps/available.js rename to js/src/views/Dapps/registry.js index 55d542017..57668c651 100644 --- a/js/src/views/Dapps/available.js +++ b/js/src/views/Dapps/registry.js @@ -18,24 +18,14 @@ import BigNumber from 'bignumber.js'; import { parityNode } from '../../environment'; -const apps = [ +const builtinApps = [ { id: '0xf9f2d620c2e08f83e45555247146c62185e4ab7cf82a4b9002a265a0d020348f', url: 'basiccoin', name: 'Token Deployment', description: 'Deploy new basic tokens that you are able to send around', author: 'Parity Team ', - version: '1.0.0', - builtin: true - }, - { - id: '0xd798a48831b4ccdbc71de206a1d6a4aa73546c7b6f59c22a47452af414dc64d6', - url: 'gavcoin', - name: 'GAVcoin', - description: 'Manage your GAVcoins, the hottest new property in crypto', - author: 'Parity Team ', - version: '1.0.0', - builtin: true + version: '1.0.0' }, { id: '0xd1adaede68d344519025e2ff574650cd99d3830fe6d274c7a7843cdc00e17938', @@ -43,8 +33,7 @@ const apps = [ name: 'Registry', description: 'A global registry of addresses on the network', author: 'Parity Team ', - version: '1.0.0', - builtin: true + version: '1.0.0' }, { id: '0x0a8048117e51e964628d0f2d26342b3cd915248b59bcce2721e1d05f5cfa2208', @@ -52,8 +41,7 @@ const apps = [ name: 'Token Registry', description: 'A registry of transactable tokens on the network', author: 'Parity Team ', - version: '1.0.0', - builtin: true + version: '1.0.0' }, { id: '0xf49089046f53f5d2e5f3513c1c32f5ff57d986e46309a42d2b249070e4e72c46', @@ -61,8 +49,7 @@ const apps = [ name: 'Method Registry', description: 'A registry of method signatures for lookups on transactions', author: 'Parity Team ', - version: '1.0.0', - builtin: true + version: '1.0.0' }, { id: '0x058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75', @@ -70,12 +57,34 @@ const apps = [ name: 'GitHub Hint', description: 'A mapping of GitHub URLs to hashes for use in contracts as references', author: 'Parity Team ', - version: '1.0.0', - builtin: true + version: '1.0.0' } ]; -export default function (api) { +// 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 function fetchAvailable (api) { + // TODO: Since we don't have an extensive GithubHint app, get the value somehow + // RESULT: 0x22cd66e1b05882c0fa17a16d252d3b3ee2238ccbac8153f69a35c83f02ca76ee + // api.ethcore + // .hashContent('https://codeload.github.com/gavofyork/gavcoin/zip/5a9f11ff2ad0d05c565a938ceffdfa0d23af9981') + // .then((sha3) => { + // console.log('archive', sha3); + // }); + return fetch(`${parityNode}/api/apps`) .then((response) => { return response.ok @@ -83,10 +92,17 @@ export default function (api) { : []; }) .catch((error) => { - console.warn('app list', error); + console.warn('fetchAvailable', error); return []; }) - .then((localApps) => { + .then((_localApps) => { + const localApps = _localApps + .filter((app) => !['ui'].includes(app.id)) + .map((app) => { + app.local = true; + return app; + }); + return api.ethcore .registryAddress() .then((registryAddress) => { @@ -94,12 +110,45 @@ export default function (api) { return []; } - return apps; + const _builtinApps = builtinApps + .map((app) => { + app.builtin = true; + return app; + }); + + return networkApps + .map((app) => { + app.network = true; + return app; + }) + .concat(_builtinApps); }) - .then((builtinApps) => { - return builtinApps - .concat(localApps.filter((app) => !['ui'].includes(app.id))) - .sort((a, b) => a.name.localeCompare(b.name)); + .then((registryApps) => { + return registryApps + .concat(localApps) + .sort((a, b) => (a.name || '').localeCompare(b.name || '')); }); + }) + .catch((error) => { + console.warn('fetchAvailable', error); + }); +} + +export function fetchManifest (app, contentHash) { + return fetch(`${parityNode}/${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('fetchManifest', error); }); }