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);
});
}