2016-12-11 19:30:54 +01:00
|
|
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
2016-11-07 15:22:46 +01:00
|
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
2016-11-11 16:53:10 +01:00
|
|
|
import { action, computed, observable, transaction } from 'mobx';
|
2016-11-14 17:02:45 +01:00
|
|
|
import store from 'store';
|
2016-11-07 15:22:46 +01:00
|
|
|
|
2016-12-05 11:47:13 +01:00
|
|
|
import Contracts from '~/contracts';
|
2016-12-11 21:03:40 +01:00
|
|
|
import {
|
|
|
|
fetchBuiltinApps, fetchLocalApps,
|
|
|
|
fetchRegistryAppIds, fetchRegistryApp,
|
|
|
|
subscribeToChanges
|
|
|
|
} from '~/util/dapps';
|
2016-11-11 16:53:10 +01:00
|
|
|
|
2016-11-14 17:02:45 +01:00
|
|
|
const LS_KEY_DISPLAY = 'displayApps';
|
2016-11-16 13:08:08 +01:00
|
|
|
const LS_KEY_EXTERNAL_ACCEPT = 'acceptExternal';
|
2016-12-11 21:03:40 +01:00
|
|
|
const BUILTIN_APPS_KEY = 'BUILTIN_APPS_KEY';
|
|
|
|
|
|
|
|
let instance = null;
|
2016-11-07 15:22:46 +01:00
|
|
|
|
|
|
|
export default class DappsStore {
|
|
|
|
@observable apps = [];
|
2016-11-14 17:02:45 +01:00
|
|
|
@observable displayApps = {};
|
2016-11-07 15:22:46 +01:00
|
|
|
@observable modalOpen = false;
|
2016-11-16 13:08:08 +01:00
|
|
|
@observable externalOverlayVisible = true;
|
2016-11-07 15:22:46 +01:00
|
|
|
|
2016-12-11 21:03:40 +01:00
|
|
|
_api = null;
|
|
|
|
_subscriptions = {};
|
|
|
|
|
|
|
|
_cachedApps = {};
|
2016-11-25 16:46:35 +01:00
|
|
|
_manifests = {};
|
2016-12-11 21:03:40 +01:00
|
|
|
_registryAppsIds = null;
|
2016-11-25 16:46:35 +01:00
|
|
|
|
2016-11-07 15:22:46 +01:00
|
|
|
constructor (api) {
|
|
|
|
this._api = api;
|
|
|
|
|
2016-12-27 12:40:33 +01:00
|
|
|
this.readDisplayApps();
|
2016-11-16 13:08:08 +01:00
|
|
|
this.loadExternalOverlay();
|
2016-12-11 21:03:40 +01:00
|
|
|
this.subscribeToChanges();
|
|
|
|
}
|
|
|
|
|
2017-01-04 15:15:25 +01:00
|
|
|
/**
|
|
|
|
* Try to find the app from the local (local or builtin)
|
|
|
|
* apps, else fetch from the node
|
|
|
|
*/
|
|
|
|
loadApp (id) {
|
2016-12-11 21:03:40 +01:00
|
|
|
const { dappReg } = Contracts.get();
|
2016-11-11 16:53:10 +01:00
|
|
|
|
2017-01-04 15:15:25 +01:00
|
|
|
return this
|
|
|
|
.loadLocalApps()
|
|
|
|
.then(() => {
|
|
|
|
const app = this.apps.find((app) => app.id === id);
|
|
|
|
|
|
|
|
if (app) {
|
|
|
|
return app;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.fetchRegistryApp(dappReg, id, true);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
loadLocalApps () {
|
|
|
|
return Promise
|
2016-11-14 17:02:45 +01:00
|
|
|
.all([
|
2016-12-11 21:03:40 +01:00
|
|
|
this.fetchBuiltinApps().then((apps) => this.addApps(apps)),
|
2017-01-04 15:15:25 +01:00
|
|
|
this.fetchLocalApps().then((apps) => this.addApps(apps))
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
loadAllApps () {
|
|
|
|
const { dappReg } = Contracts.get();
|
|
|
|
|
|
|
|
return Promise
|
|
|
|
.all([
|
|
|
|
this.loadLocalApps(),
|
2016-12-11 21:03:40 +01:00
|
|
|
this.fetchRegistryApps(dappReg).then((apps) => this.addApps(apps))
|
2016-11-14 17:02:45 +01:00
|
|
|
])
|
2016-11-14 21:57:28 +01:00
|
|
|
.then(this.writeDisplayApps);
|
2016-11-07 15:22:46 +01:00
|
|
|
}
|
|
|
|
|
2016-12-11 21:03:40 +01:00
|
|
|
static get (api) {
|
|
|
|
if (!instance) {
|
|
|
|
instance = new DappsStore(api);
|
|
|
|
}
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
subscribeToChanges () {
|
|
|
|
const { dappReg } = Contracts.get();
|
|
|
|
|
|
|
|
// Unsubscribe from previous subscriptions, if any
|
|
|
|
if (this._subscriptions.block) {
|
|
|
|
this._api.unsubscribe(this._subscriptions.block);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._subscriptions.filter) {
|
|
|
|
this._api.eth.uninstallFilter(this._subscriptions.filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Subscribe to dapps reg changes
|
|
|
|
subscribeToChanges(this._api, dappReg, (appIds) => {
|
|
|
|
const updates = appIds.map((appId) => {
|
|
|
|
return this.fetchRegistryApp(dappReg, appId, true);
|
|
|
|
});
|
|
|
|
|
|
|
|
Promise
|
|
|
|
.all(updates)
|
|
|
|
.then((apps) => {
|
|
|
|
this.addApps(apps);
|
|
|
|
});
|
|
|
|
}).then((subscriptions) => {
|
|
|
|
this._subscriptions = subscriptions;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchBuiltinApps (force = false) {
|
|
|
|
if (!force && this._cachedApps[BUILTIN_APPS_KEY] !== undefined) {
|
|
|
|
return Promise.resolve(this._cachedApps[BUILTIN_APPS_KEY]);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._cachedApps[BUILTIN_APPS_KEY] = fetchBuiltinApps()
|
|
|
|
.then((apps) => {
|
|
|
|
this._cachedApps[BUILTIN_APPS_KEY] = apps;
|
|
|
|
return apps;
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.resolve(this._cachedApps[BUILTIN_APPS_KEY]);
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchLocalApps () {
|
|
|
|
return fetchLocalApps(this._api);
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchRegistryAppIds (force = false) {
|
|
|
|
if (!force && this._registryAppsIds) {
|
|
|
|
return Promise.resolve(this._registryAppsIds);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._registryAppsIds = fetchRegistryAppIds()
|
|
|
|
.then((appIds) => {
|
|
|
|
this._registryAppsIds = appIds;
|
|
|
|
return this._registryAppsIds;
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.resolve(this._registryAppsIds);
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchRegistryApp (dappReg, appId, force = false) {
|
|
|
|
if (!force && this._cachedApps[appId] !== undefined) {
|
|
|
|
return Promise.resolve(this._cachedApps[appId]);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._cachedApps[appId] = fetchRegistryApp(this._api, dappReg, appId)
|
|
|
|
.then((dapp) => {
|
|
|
|
this._cachedApps[appId] = dapp;
|
|
|
|
return dapp;
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.resolve(this._cachedApps[appId]);
|
|
|
|
}
|
|
|
|
|
|
|
|
fetchRegistryApps (dappReg) {
|
|
|
|
return this
|
|
|
|
.fetchRegistryAppIds()
|
|
|
|
.then((appIds) => {
|
|
|
|
const promises = appIds.map((appId) => {
|
|
|
|
// Fetch the Dapp and display it ASAP
|
|
|
|
return this
|
|
|
|
.fetchRegistryApp(dappReg, appId)
|
|
|
|
.then((app) => {
|
|
|
|
if (app) {
|
|
|
|
this.addApps([ app ]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return app;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.all(promises);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-11-14 21:57:28 +01:00
|
|
|
@computed get sortedBuiltin () {
|
|
|
|
return this.apps.filter((app) => app.type === 'builtin');
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed get sortedLocal () {
|
|
|
|
return this.apps.filter((app) => app.type === 'local');
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed get sortedNetwork () {
|
|
|
|
return this.apps.filter((app) => app.type === 'network');
|
2016-11-14 17:02:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@computed get visibleApps () {
|
2016-11-14 21:57:28 +01:00
|
|
|
return this.apps.filter((app) => this.displayApps[app.id] && this.displayApps[app.id].visible);
|
2016-11-07 15:22:46 +01:00
|
|
|
}
|
|
|
|
|
2016-11-16 12:36:15 +01:00
|
|
|
@computed get visibleBuiltin () {
|
|
|
|
return this.visibleApps.filter((app) => app.type === 'builtin');
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed get visibleLocal () {
|
|
|
|
return this.visibleApps.filter((app) => app.type === 'local');
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed get visibleNetwork () {
|
|
|
|
return this.visibleApps.filter((app) => app.type === 'network');
|
|
|
|
}
|
|
|
|
|
2016-11-07 15:22:46 +01:00
|
|
|
@action openModal = () => {
|
|
|
|
this.modalOpen = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@action closeModal = () => {
|
|
|
|
this.modalOpen = false;
|
|
|
|
}
|
|
|
|
|
2016-11-16 13:08:08 +01:00
|
|
|
@action closeExternalOverlay = () => {
|
|
|
|
this.externalOverlayVisible = false;
|
|
|
|
store.set(LS_KEY_EXTERNAL_ACCEPT, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
@action loadExternalOverlay () {
|
|
|
|
this.externalOverlayVisible = !(store.get(LS_KEY_EXTERNAL_ACCEPT) || false);
|
|
|
|
}
|
|
|
|
|
2016-11-07 15:22:46 +01:00
|
|
|
@action hideApp = (id) => {
|
2016-11-14 21:57:28 +01:00
|
|
|
this.displayApps = Object.assign({}, this.displayApps, { [id]: { visible: false } });
|
|
|
|
this.writeDisplayApps();
|
2016-11-07 15:22:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@action showApp = (id) => {
|
2016-11-14 21:57:28 +01:00
|
|
|
this.displayApps = Object.assign({}, this.displayApps, { [id]: { visible: true } });
|
|
|
|
this.writeDisplayApps();
|
|
|
|
}
|
2016-11-14 17:02:45 +01:00
|
|
|
|
2016-11-14 21:57:28 +01:00
|
|
|
@action readDisplayApps = () => {
|
|
|
|
this.displayApps = store.get(LS_KEY_DISPLAY) || {};
|
2016-11-14 17:02:45 +01:00
|
|
|
}
|
|
|
|
|
2016-11-14 21:57:28 +01:00
|
|
|
@action writeDisplayApps = () => {
|
|
|
|
store.set(LS_KEY_DISPLAY, this.displayApps);
|
2016-11-14 17:02:45 +01:00
|
|
|
}
|
|
|
|
|
2016-12-11 21:03:40 +01:00
|
|
|
@action addApps = (_apps) => {
|
2016-11-14 17:02:45 +01:00
|
|
|
transaction(() => {
|
2016-12-11 21:03:40 +01:00
|
|
|
const apps = _apps.filter((app) => app);
|
|
|
|
|
|
|
|
// Get new apps IDs if available
|
|
|
|
const newAppsIds = apps
|
|
|
|
.map((app) => app.id)
|
|
|
|
.filter((id) => id);
|
|
|
|
|
2016-11-14 21:57:28 +01:00
|
|
|
this.apps = this.apps
|
2016-12-11 21:03:40 +01:00
|
|
|
.filter((app) => !app.id || !newAppsIds.includes(app.id))
|
2016-11-14 21:57:28 +01:00
|
|
|
.concat(apps || [])
|
|
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
|
|
|
|
const visibility = {};
|
|
|
|
apps.forEach((app) => {
|
|
|
|
if (!this.displayApps[app.id]) {
|
|
|
|
visibility[app.id] = { visible: app.visible };
|
|
|
|
}
|
|
|
|
});
|
2016-11-14 17:02:45 +01:00
|
|
|
|
2016-11-14 21:57:28 +01:00
|
|
|
this.displayApps = Object.assign({}, this.displayApps, visibility);
|
2016-11-14 17:02:45 +01:00
|
|
|
});
|
2016-11-07 15:22:46 +01:00
|
|
|
}
|
|
|
|
}
|