Update with latest dapps
This commit is contained in:
@@ -19,7 +19,7 @@ import { observer } from 'mobx-react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import DappsStore from '../Dapps/dappsStore';
|
||||
import DappsStore from '@parity/shared/mobx/dappsStore';
|
||||
|
||||
import styles from './dapp.css';
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Button } from '@parity/ui';
|
||||
|
||||
import DappsStore from '../../Dapps/dappsStore';
|
||||
import DappsStore from '@parity/shared/mobx/dappsStore';
|
||||
|
||||
export default function Request ({ appId, className, approveRequest, denyRequest, queueId, request: { from, method } }) {
|
||||
const _onApprove = () => approveRequest(queueId, false);
|
||||
|
||||
@@ -20,7 +20,7 @@ import store from 'store';
|
||||
|
||||
import { sha3 } from '@parity/api/util/sha3';
|
||||
|
||||
import VisibleStore from '../Dapps/dappsStore';
|
||||
import VisibleStore from '@parity/shared/mobx/dappsStore';
|
||||
import filteredRequests from './filteredRequests';
|
||||
|
||||
const LS_PERMISSIONS = '_parity::dapps::methods';
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import sinon from 'sinon';
|
||||
import localStore from 'store';
|
||||
|
||||
import Contracts from '@parity/shared/contracts';
|
||||
|
||||
import Store, { LS_KEY_DISPLAY } from './dappsStore';
|
||||
|
||||
const APPID_DAPPREG = '0x7bbc4f1a27628781b96213e781a1b8eec6982c1db8fac739af6e4c5a55862c03';
|
||||
const APPID_GHH = '0x058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75';
|
||||
const APPID_LOCALTX = '0xae74ad174b95cdbd01c88ac5b73a296d33e9088fc2a200e76bcedf3a94a7815d';
|
||||
const APPID_TOKENDEPLOY = '0xf9f2d620c2e08f83e45555247146c62185e4ab7cf82a4b9002a265a0d020348f';
|
||||
|
||||
let globalContractsGet;
|
||||
|
||||
function stubGlobals () {
|
||||
globalContractsGet = Contracts.get;
|
||||
|
||||
Contracts.get = () => {
|
||||
return {
|
||||
dappReg: {
|
||||
at: sinon.stub().resolves([[0, 1, 2, 3], 'appOwner']),
|
||||
count: sinon.stub().resolves(new BigNumber(1)),
|
||||
getContract: sinon.stub().resolves({}),
|
||||
getContent: sinon.stub().resolves([0, 1, 2, 3]),
|
||||
getImage: sinon.stub().resolves([0, 1, 2, 3]),
|
||||
getManifest: sinon.stub().resolves([0, 1, 2, 3])
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function restoreGlobals () {
|
||||
Contracts.get = globalContractsGet;
|
||||
}
|
||||
|
||||
let api;
|
||||
let store;
|
||||
|
||||
function create () {
|
||||
api = {
|
||||
parity: {
|
||||
dappsList: () => Promise.resolve([])
|
||||
}
|
||||
};
|
||||
store = new Store(api);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
describe('Dapps/DappStore', () => {
|
||||
beforeEach(() => {
|
||||
stubGlobals();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
restoreGlobals();
|
||||
});
|
||||
|
||||
describe('@action', () => {
|
||||
const defaultViews = {
|
||||
[APPID_TOKENDEPLOY]: { visible: false },
|
||||
[APPID_DAPPREG]: { visible: true }
|
||||
};
|
||||
|
||||
describe('setDisplayApps', () => {
|
||||
beforeEach(() => {
|
||||
create();
|
||||
store.setDisplayApps(defaultViews);
|
||||
});
|
||||
|
||||
it('sets from empty start', () => {
|
||||
expect(store.displayApps).to.deep.equal(defaultViews);
|
||||
});
|
||||
|
||||
it('overrides single keys, keeping existing', () => {
|
||||
store.setDisplayApps({ [APPID_TOKENDEPLOY]: { visible: true } });
|
||||
|
||||
expect(store.displayApps).to.deep.equal(
|
||||
Object.assign({}, defaultViews, { [APPID_TOKENDEPLOY]: { visible: true } })
|
||||
);
|
||||
});
|
||||
|
||||
it('extends with new keys, keeping existing', () => {
|
||||
store.setDisplayApps({ 'test': { visible: true } });
|
||||
|
||||
expect(store.displayApps).to.deep.equal(
|
||||
Object.assign({}, defaultViews, { 'test': { visible: true } })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hideApp/showApp', () => {
|
||||
beforeEach(() => {
|
||||
localStore.set(LS_KEY_DISPLAY, defaultViews);
|
||||
|
||||
create().readDisplayApps();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
localStore.set(LS_KEY_DISPLAY, {});
|
||||
});
|
||||
|
||||
it('disables visibility', () => {
|
||||
store.hideApp(APPID_DAPPREG);
|
||||
|
||||
expect(store.displayApps[APPID_DAPPREG].visible).to.be.false;
|
||||
expect(localStore.get(LS_KEY_DISPLAY)).to.deep.equal(
|
||||
Object.assign({}, defaultViews, { [APPID_DAPPREG]: { visible: false } })
|
||||
);
|
||||
});
|
||||
|
||||
it('enables visibility', () => {
|
||||
store.showApp(APPID_TOKENDEPLOY);
|
||||
|
||||
expect(store.displayApps[APPID_TOKENDEPLOY].visible).to.be.true;
|
||||
expect(localStore.get(LS_KEY_DISPLAY)).to.deep.equal(
|
||||
Object.assign({}, defaultViews, { [APPID_TOKENDEPLOY]: { visible: true } })
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps visibility state', () => {
|
||||
store.hideApp(APPID_TOKENDEPLOY);
|
||||
store.showApp(APPID_DAPPREG);
|
||||
|
||||
expect(store.displayApps[APPID_TOKENDEPLOY].visible).to.be.false;
|
||||
expect(store.displayApps[APPID_DAPPREG].visible).to.be.true;
|
||||
expect(localStore.get(LS_KEY_DISPLAY)).to.deep.equal(defaultViews);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readDisplayApps/writeDisplayApps', () => {
|
||||
beforeEach(() => {
|
||||
localStore.set(LS_KEY_DISPLAY, defaultViews);
|
||||
|
||||
create().readDisplayApps();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
localStore.set(LS_KEY_DISPLAY, {});
|
||||
});
|
||||
|
||||
it('loads visibility from storage', () => {
|
||||
expect(store.displayApps).to.deep.equal(defaultViews);
|
||||
});
|
||||
|
||||
it('saves visibility to storage', () => {
|
||||
store.setDisplayApps({ [APPID_TOKENDEPLOY]: { visible: true } });
|
||||
store.writeDisplayApps();
|
||||
|
||||
expect(localStore.get(LS_KEY_DISPLAY)).to.deep.equal(
|
||||
Object.assign({}, defaultViews, { [APPID_TOKENDEPLOY]: { visible: true } })
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('saved views', () => {
|
||||
beforeEach(() => {
|
||||
localStore.set(LS_KEY_DISPLAY, {
|
||||
[APPID_TOKENDEPLOY]: { visible: false },
|
||||
[APPID_DAPPREG]: { visible: true }
|
||||
});
|
||||
|
||||
return create().loadAllApps();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
localStore.set(LS_KEY_DISPLAY, {});
|
||||
});
|
||||
|
||||
it('disables based on saved keys', () => {
|
||||
expect(store.displayApps[APPID_TOKENDEPLOY].visible).to.be.false;
|
||||
});
|
||||
|
||||
it('enables based on saved keys', () => {
|
||||
expect(store.displayApps[APPID_DAPPREG].visible).to.be.true;
|
||||
});
|
||||
|
||||
it('keeps non-sepcified disabled keys', () => {
|
||||
expect(store.displayApps[APPID_GHH].visible).to.be.false;
|
||||
});
|
||||
|
||||
it('keeps non-specified enabled keys', () => {
|
||||
expect(store.displayApps[APPID_LOCALTX].visible).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -23,7 +23,7 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import { Checkbox, DappCard, Page, SectionList } from '@parity/ui';
|
||||
|
||||
import DappsStore from './dappsStore';
|
||||
import DappsStore from '@parity/shared/mobx/dappsStore';
|
||||
|
||||
import styles from './dapps.css';
|
||||
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { action, computed, observable, transaction } from 'mobx';
|
||||
import store from 'store';
|
||||
|
||||
import Contracts from '@parity/shared/contracts';
|
||||
import { fetchBuiltinApps, fetchLocalApps, fetchRegistryAppIds, fetchRegistryApp, subscribeToChanges } from '@parity/shared/util/dapps';
|
||||
|
||||
const LS_KEY_DISPLAY = 'displayApps';
|
||||
const LS_KEY_EXTERNAL_ACCEPT = 'acceptExternal';
|
||||
const BUILTIN_APPS_KEY = 'BUILTIN_APPS_KEY';
|
||||
|
||||
let instance = null;
|
||||
|
||||
export default class DappsStore extends EventEmitter {
|
||||
@observable apps = [];
|
||||
@observable displayApps = {};
|
||||
@observable modalOpen = false;
|
||||
@observable externalOverlayVisible = true;
|
||||
|
||||
_api = null;
|
||||
_subscriptions = {};
|
||||
_cachedApps = {};
|
||||
_manifests = {};
|
||||
_registryAppsIds = null;
|
||||
|
||||
constructor (api) {
|
||||
super();
|
||||
|
||||
this._api = api;
|
||||
|
||||
this.readDisplayApps();
|
||||
this.loadExternalOverlay();
|
||||
this.subscribeToChanges();
|
||||
}
|
||||
|
||||
static get (api) {
|
||||
if (!instance) {
|
||||
instance = new DappsStore(api);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@computed get allApps () {
|
||||
return this.apps;
|
||||
}
|
||||
|
||||
@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');
|
||||
}
|
||||
|
||||
@computed get visibleApps () {
|
||||
return this.apps.filter((app) => this.displayApps[app.id] && this.displayApps[app.id].visible);
|
||||
}
|
||||
|
||||
@computed get visibleBuiltin () {
|
||||
return this.visibleApps.filter((app) => !app.noselect && 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');
|
||||
}
|
||||
|
||||
@computed get visibleViews () {
|
||||
return this.visibleApps.filter((app) => !app.noselect && app.type === 'view');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find the app from the local (local or builtin)
|
||||
* apps, else fetch from the node
|
||||
*/
|
||||
loadApp (id) {
|
||||
const { dappReg } = Contracts.get(this._api);
|
||||
|
||||
return this
|
||||
.loadLocalApps()
|
||||
.then(() => {
|
||||
const app = this.apps.find((app) => app.id === id);
|
||||
|
||||
if (app) {
|
||||
return app;
|
||||
}
|
||||
|
||||
return this.fetchRegistryApp(dappReg, id, true);
|
||||
})
|
||||
.then((app) => {
|
||||
this.emit('loaded', app);
|
||||
return app;
|
||||
});
|
||||
}
|
||||
|
||||
loadLocalApps () {
|
||||
return Promise
|
||||
.all([
|
||||
this.fetchBuiltinApps().then((apps) => this.addApps(apps)),
|
||||
this.fetchLocalApps().then((apps) => this.addApps(apps))
|
||||
]);
|
||||
}
|
||||
|
||||
loadAllApps () {
|
||||
const { dappReg } = Contracts.get(this._api);
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
this.loadLocalApps(),
|
||||
this.fetchRegistryApps(dappReg).then((apps) => this.addApps(apps))
|
||||
])
|
||||
.then(this.writeDisplayApps);
|
||||
}
|
||||
|
||||
subscribeToChanges () {
|
||||
const { dappReg } = Contracts.get(this._api);
|
||||
|
||||
// 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(this._api)
|
||||
.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(this._api)
|
||||
.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);
|
||||
});
|
||||
}
|
||||
|
||||
@action openModal = () => {
|
||||
this.modalOpen = true;
|
||||
}
|
||||
|
||||
@action closeModal = () => {
|
||||
this.modalOpen = false;
|
||||
}
|
||||
|
||||
@action closeExternalOverlay = () => {
|
||||
this.externalOverlayVisible = false;
|
||||
store.set(LS_KEY_EXTERNAL_ACCEPT, true);
|
||||
}
|
||||
|
||||
@action loadExternalOverlay () {
|
||||
this.externalOverlayVisible = !(store.get(LS_KEY_EXTERNAL_ACCEPT) || false);
|
||||
}
|
||||
|
||||
@action hideApp = (id) => {
|
||||
this.setDisplayApps({ [id]: { visible: false } });
|
||||
this.writeDisplayApps();
|
||||
}
|
||||
|
||||
@action showApp = (id) => {
|
||||
this.setDisplayApps({ [id]: { visible: true } });
|
||||
this.writeDisplayApps();
|
||||
}
|
||||
|
||||
@action readDisplayApps = () => {
|
||||
this.displayApps = store.get(LS_KEY_DISPLAY) || {};
|
||||
}
|
||||
|
||||
@action writeDisplayApps = () => {
|
||||
store.set(LS_KEY_DISPLAY, this.displayApps);
|
||||
}
|
||||
|
||||
@action setDisplayApps = (displayApps) => {
|
||||
this.displayApps = Object.assign({}, this.displayApps, displayApps);
|
||||
};
|
||||
|
||||
@action addApps = (_apps = []) => {
|
||||
transaction(() => {
|
||||
const apps = _apps.filter((app) => app);
|
||||
|
||||
// Get new apps IDs if available
|
||||
const newAppsIds = apps
|
||||
.map((app) => app.id)
|
||||
.filter((id) => id);
|
||||
|
||||
this.apps = this.apps
|
||||
.filter((app) => !app.id || !newAppsIds.includes(app.id))
|
||||
.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 };
|
||||
}
|
||||
});
|
||||
|
||||
this.setDisplayApps(visibility);
|
||||
});
|
||||
}
|
||||
|
||||
getAppById = (id) => {
|
||||
return this.apps.find((app) => app.id === id);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
LS_KEY_DISPLAY
|
||||
};
|
||||
@@ -21,7 +21,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
import { Button } from '@parity/ui';
|
||||
import { CloseIcon, CheckIcon } from '@parity/ui/Icons';
|
||||
|
||||
import Store from './store';
|
||||
import Store from '@parity/shared/mobx/extensionStore';
|
||||
import styles from './extension.css';
|
||||
|
||||
@observer
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/* global chrome */
|
||||
|
||||
import { action, computed, observable } from 'mobx';
|
||||
|
||||
import store from 'store';
|
||||
import browser from 'useragent.js/lib/browser';
|
||||
|
||||
import { DOMAIN } from '@parity/shared/util/constants';
|
||||
|
||||
const A_DAY = 24 * 60 * 60 * 1000;
|
||||
const NEXT_DISPLAY = '_parity::extensionWarning::nextDisplay';
|
||||
|
||||
// 'https://chrome.google.com/webstore/detail/parity-ethereum-integrati/himekenlppkgeaoeddcliojfddemadig';
|
||||
const EXTENSION_PAGE = 'https://chrome.google.com/webstore/detail/himekenlppkgeaoeddcliojfddemadig';
|
||||
|
||||
let instance;
|
||||
|
||||
export default class Store {
|
||||
@observable hasExtension = false;
|
||||
@observable isInstalling = false;
|
||||
@observable nextDisplay = 0;
|
||||
@observable shouldInstall = false;
|
||||
|
||||
constructor () {
|
||||
this.nextDisplay = store.get(NEXT_DISPLAY) || 0;
|
||||
this.testInstall();
|
||||
}
|
||||
|
||||
@computed get showWarning () {
|
||||
return !this.isInstalling && this.shouldInstall && (Date.now() > this.nextDisplay);
|
||||
}
|
||||
|
||||
@action setExtensionActive = () => {
|
||||
this.hasExtension = true;
|
||||
}
|
||||
|
||||
@action setInstalling = (isInstalling) => {
|
||||
this.isInstalling = isInstalling;
|
||||
}
|
||||
|
||||
@action snoozeWarning = (sleep = A_DAY) => {
|
||||
this.nextDisplay = Date.now() + sleep;
|
||||
store.set(NEXT_DISPLAY, this.nextDisplay);
|
||||
}
|
||||
|
||||
@action testInstall = () => {
|
||||
this.shouldInstall = this.readStatus();
|
||||
}
|
||||
|
||||
readStatus = () => {
|
||||
const hasExtension = Symbol.for('parity.extension') in window;
|
||||
const ua = browser.analyze(navigator.userAgent || '');
|
||||
|
||||
if (hasExtension) {
|
||||
this.setExtensionActive();
|
||||
return false;
|
||||
}
|
||||
|
||||
return (ua || {}).name.toLowerCase() === 'chrome';
|
||||
}
|
||||
|
||||
installExtension = () => {
|
||||
this.setInstalling(true);
|
||||
|
||||
if (window.location.hostname.toString().endsWith(DOMAIN)) {
|
||||
return this.inlineInstall()
|
||||
.catch((error) => {
|
||||
console.warn('Unable to perform direct install', error);
|
||||
window.open(EXTENSION_PAGE, '_blank');
|
||||
});
|
||||
}
|
||||
|
||||
window.open(EXTENSION_PAGE, '_blank');
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
inlineInstall = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const link = document.createElement('link');
|
||||
|
||||
link.setAttribute('rel', 'chrome-webstore-item');
|
||||
link.setAttribute('href', EXTENSION_PAGE);
|
||||
document.querySelector('head').appendChild(link);
|
||||
|
||||
if (chrome && chrome.webstore && chrome.webstore.install) {
|
||||
chrome.webstore.install(EXTENSION_PAGE, resolve, reject);
|
||||
} else {
|
||||
reject(new Error('Direct installation failed.'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new Store();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import { CancelIcon, FingerprintIcon } from '@parity/ui/Icons';
|
||||
|
||||
import imagesEthcoreBlock from '@parity/shared/assets/images/parity-logo-white-no-text.svg';
|
||||
|
||||
import DappsStore from '../Dapps/dappsStore';
|
||||
import DappsStore from '@parity/shared/mobx/dappsStore';
|
||||
import Signer from '../Signer/Embedded';
|
||||
|
||||
import AccountStore from './accountStore';
|
||||
|
||||
@@ -23,10 +23,9 @@ import { bindActionCreators } from 'redux';
|
||||
|
||||
import * as RequestsActions from '@parity/shared/redux/providers/signerActions';
|
||||
import { Container } from '@parity/ui';
|
||||
import RequestPending from '@parity/ui/Signer/RequestPending';
|
||||
|
||||
import Store from '../store';
|
||||
|
||||
import RequestPending from '../components/RequestPending';
|
||||
import Store from '@parity/shared/mobx/signerStore';
|
||||
|
||||
import styles from './embedded.css';
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.container {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import styles from './accountLink.css';
|
||||
|
||||
class AccountLink extends Component {
|
||||
static propTypes = {
|
||||
accountAddresses: PropTypes.array.isRequired,
|
||||
address: PropTypes.string.isRequired,
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
externalLink: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
state = {
|
||||
link: null
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
const { address, externalLink } = this.props;
|
||||
|
||||
this.updateLink(address, externalLink);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
const { address, externalLink } = nextProps;
|
||||
|
||||
this.updateLink(address, externalLink);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children, address, className, externalLink } = this.props;
|
||||
|
||||
if (externalLink) {
|
||||
return (
|
||||
<a
|
||||
href={ this.state.link }
|
||||
target='_blank'
|
||||
className={ `${styles.container} ${className}` }
|
||||
>
|
||||
{ children || address }
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={ `${styles.container} ${className}` }
|
||||
to={ this.state.link }
|
||||
>
|
||||
{ children || address }
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
updateLink (address, externalLink) {
|
||||
const { accountAddresses } = this.props;
|
||||
const isAccount = accountAddresses.includes(address);
|
||||
|
||||
let link = isAccount
|
||||
? `/accounts/${address}`
|
||||
: `/addresses/${address}`;
|
||||
|
||||
if (externalLink) {
|
||||
const path = externalLink.replace(/\/+$/, '');
|
||||
|
||||
link = `${path}/#${link}`;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
link
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (initState) {
|
||||
const { accounts } = initState.personal;
|
||||
const accountAddresses = Object.keys(accounts);
|
||||
|
||||
return () => {
|
||||
return { accountAddresses };
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(AccountLink);
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './accountLink';
|
||||
@@ -1,50 +0,0 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.acc {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.acc > * {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.address {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.acc img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
vertical-align: middle;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.name {
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
text-transform: uppercase;
|
||||
|
||||
span {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { IdentityIcon, IdentityName } from '@parity/ui';
|
||||
|
||||
import AccountLink from './AccountLink';
|
||||
|
||||
import styles from './account.css';
|
||||
|
||||
export default class Account extends Component {
|
||||
static propTypes = {
|
||||
address: PropTypes.string.isRequired,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
externalLink: PropTypes.string.isRequired,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
balance: PropTypes.object // eth BigNumber, not required since it mght take time to fetch
|
||||
};
|
||||
|
||||
state = {
|
||||
balanceDisplay: '?'
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
this.updateBalanceDisplay(this.props.balance);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.balance === this.props.balance) {
|
||||
return;
|
||||
}
|
||||
this.updateBalanceDisplay(nextProps.balance);
|
||||
}
|
||||
|
||||
updateBalanceDisplay (balance) {
|
||||
this.setState({
|
||||
balanceDisplay: balance ? balance.div(1e18).toFormat(3) : '?'
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const { address, className, disabled, externalLink, netVersion } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ `${styles.acc} ${className}` }>
|
||||
<AccountLink
|
||||
address={ address }
|
||||
externalLink={ externalLink }
|
||||
netVersion={ netVersion }
|
||||
>
|
||||
<IdentityIcon
|
||||
center
|
||||
disabled={ disabled }
|
||||
address={ address }
|
||||
/>
|
||||
</AccountLink>
|
||||
{ this.renderName() }
|
||||
{ this.renderBalance() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderBalance () {
|
||||
const { balanceDisplay } = this.state;
|
||||
|
||||
return (
|
||||
<span> <strong>{ balanceDisplay }</strong> <small>ETH</small></span>
|
||||
);
|
||||
}
|
||||
|
||||
renderName () {
|
||||
const { address, externalLink, netVersion } = this.props;
|
||||
const name = <IdentityName address={ address } empty />;
|
||||
|
||||
if (!name) {
|
||||
return (
|
||||
<AccountLink
|
||||
address={ address }
|
||||
externalLink={ externalLink }
|
||||
netVersion={ netVersion }
|
||||
>
|
||||
[{ this.shortAddress(address) }]
|
||||
</AccountLink>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AccountLink
|
||||
address={ address }
|
||||
externalLink={ externalLink }
|
||||
netVersion={ netVersion }
|
||||
>
|
||||
<span>
|
||||
<span className={ styles.name }>{ name }</span>
|
||||
<span className={ styles.address }>[{ this.tinyAddress(address) }]</span>
|
||||
</span>
|
||||
</AccountLink>
|
||||
);
|
||||
}
|
||||
|
||||
tinyAddress () {
|
||||
const { address } = this.props;
|
||||
const len = address.length;
|
||||
|
||||
return address.slice(2, 4) + '..' + address.slice(len - 2);
|
||||
}
|
||||
|
||||
shortAddress () {
|
||||
const { address } = this.props;
|
||||
const len = address.length;
|
||||
|
||||
return address.slice(2, 8) + '..' + address.slice(len - 7);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './account';
|
||||
@@ -1,185 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Account from '../Account';
|
||||
import TransactionPendingForm from '../TransactionPendingForm';
|
||||
import RequestOrigin from '../RequestOrigin';
|
||||
|
||||
import styles from '../SignRequest/signRequest.css';
|
||||
|
||||
@observer
|
||||
class DecryptRequest extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
address: PropTypes.string.isRequired,
|
||||
data: PropTypes.string.isRequired,
|
||||
id: PropTypes.object.isRequired,
|
||||
isFinished: PropTypes.bool.isRequired,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
signerStore: PropTypes.object.isRequired,
|
||||
|
||||
className: PropTypes.string,
|
||||
focus: PropTypes.bool,
|
||||
isSending: PropTypes.bool,
|
||||
onConfirm: PropTypes.func,
|
||||
onReject: PropTypes.func,
|
||||
origin: PropTypes.any,
|
||||
status: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
focus: false,
|
||||
origin: {
|
||||
type: 'unknown',
|
||||
details: ''
|
||||
}
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
const { address, signerStore } = this.props;
|
||||
|
||||
signerStore.fetchBalance(address);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ `${styles.container} ${className}` }>
|
||||
{ this.renderDetails() }
|
||||
{ this.renderActions() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderDetails () {
|
||||
const { api } = this.context;
|
||||
const { address, data, netVersion, origin, signerStore } = this.props;
|
||||
const { balances, externalLink } = signerStore;
|
||||
|
||||
const balance = balances[address];
|
||||
|
||||
if (!balance) {
|
||||
return <div />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.signDetails }>
|
||||
<div className={ styles.address }>
|
||||
<Account
|
||||
address={ address }
|
||||
balance={ balance }
|
||||
className={ styles.account }
|
||||
externalLink={ externalLink }
|
||||
netVersion={ netVersion }
|
||||
/>
|
||||
<RequestOrigin origin={ origin } />
|
||||
</div>
|
||||
<div className={ styles.info } title={ api.util.sha3(data) }>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='signer.decryptRequest.request'
|
||||
defaultMessage='A request to decrypt data using your account:'
|
||||
/>
|
||||
</p>
|
||||
|
||||
<div className={ styles.signData }>
|
||||
<p>{ data }</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderActions () {
|
||||
const { accounts, address, focus, isFinished, status, data } = this.props;
|
||||
const account = accounts[address];
|
||||
|
||||
if (isFinished) {
|
||||
if (status === 'confirmed') {
|
||||
return (
|
||||
<div className={ styles.actions }>
|
||||
<span className={ styles.isConfirmed }>
|
||||
<FormattedMessage
|
||||
id='signer.decryptRequest.state.confirmed'
|
||||
defaultMessage='Confirmed'
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.actions }>
|
||||
<span className={ styles.isRejected }>
|
||||
<FormattedMessage
|
||||
id='signer.decryptRequest.state.rejected'
|
||||
defaultMessage='Rejected'
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TransactionPendingForm
|
||||
account={ account }
|
||||
address={ address }
|
||||
focus={ focus }
|
||||
isSending={ this.props.isSending }
|
||||
netVersion={ this.props.netVersion }
|
||||
onConfirm={ this.onConfirm }
|
||||
onReject={ this.onReject }
|
||||
className={ styles.actions }
|
||||
dataToSign={ { decrypt: data } }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
onConfirm = (data) => {
|
||||
const { id } = this.props;
|
||||
const { password, decrypted, wallet } = data;
|
||||
|
||||
this.props.onConfirm({ id, password, decrypted, wallet });
|
||||
}
|
||||
|
||||
onReject = () => {
|
||||
this.props.onReject(this.props.id);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { accounts } = state.personal;
|
||||
|
||||
return {
|
||||
accounts
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(DecryptRequest);
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './decryptRequest';
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './requestOrigin';
|
||||
@@ -1,43 +0,0 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
text-align: left;
|
||||
margin: 3em .5em;
|
||||
opacity: 0.6;
|
||||
font-size: 0.8em;
|
||||
|
||||
.unknown {
|
||||
color: #e44;
|
||||
}
|
||||
|
||||
.url {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.hash {
|
||||
margin-left: .5em;
|
||||
}
|
||||
|
||||
.hash, .url {
|
||||
margin-bottom: -.2em;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import IdentityIcon from '@parity/ui/IdentityIcon';
|
||||
|
||||
import styles from './requestOrigin.css';
|
||||
|
||||
export default class RequestOrigin extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
origin: PropTypes.shape({
|
||||
type: PropTypes.oneOf(['unknown', 'dapp', 'rpc', 'ipc', 'signer']),
|
||||
details: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.shape({
|
||||
session: PropTypes.string.isRequired
|
||||
})
|
||||
]).isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
render () {
|
||||
const { origin } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
Requested { this.renderOrigin(origin) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderOrigin (origin) {
|
||||
if (origin.type === 'unknown') {
|
||||
return (
|
||||
<span className={ styles.unknown }>
|
||||
<FormattedMessage
|
||||
id='signer.requestOrigin.unknownInterface'
|
||||
defaultMessage='via unknown interface'
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (origin.type === 'dapp') {
|
||||
return (
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='signer.requestOrigin.dapp'
|
||||
defaultMessage='by a dapp at {url}'
|
||||
values={ {
|
||||
url: (
|
||||
<span className={ styles.url }>
|
||||
{
|
||||
origin.details || (
|
||||
<FormattedMessage
|
||||
id='signer.requestOrigin.unknownUrl'
|
||||
defaultMessage='unknown URL'
|
||||
/>
|
||||
)
|
||||
}
|
||||
</span>
|
||||
)
|
||||
} }
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (origin.type === 'rpc') {
|
||||
return (
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='signer.requestOrigin.rpc'
|
||||
defaultMessage='via RPC {url}'
|
||||
values={ {
|
||||
url: (
|
||||
<span className={ styles.url }>
|
||||
({
|
||||
origin.details || (
|
||||
<FormattedMessage
|
||||
id='signer.requestOrigin.unknownRpc'
|
||||
defaultMessage='unidentified'
|
||||
/>
|
||||
)
|
||||
})
|
||||
</span>
|
||||
)
|
||||
} }
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (origin.type === 'ipc') {
|
||||
return (
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='signer.requestOrigin.ipc'
|
||||
defaultMessage='via IPC session'
|
||||
/>
|
||||
<span
|
||||
className={ styles.hash }
|
||||
title={ origin.details }
|
||||
>
|
||||
<IdentityIcon
|
||||
address={ origin.details }
|
||||
tiny
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (origin.type === 'signer') {
|
||||
const session = origin.details && origin.details.session || origin.details;
|
||||
|
||||
return this.renderSigner(session);
|
||||
}
|
||||
}
|
||||
|
||||
renderSigner (session) {
|
||||
if (session.substr(2) === this.context.api.transport.sessionHash) {
|
||||
return (
|
||||
<span title={ session }>
|
||||
<FormattedMessage
|
||||
id='signer.requestOrigin.signerCurrent'
|
||||
defaultMessage='via current tab'
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='signer.requestOrigin.signerUI'
|
||||
defaultMessage='via UI session'
|
||||
/>
|
||||
<span
|
||||
className={ styles.hash }
|
||||
title={ `UI Session id: ${session}` }
|
||||
>
|
||||
<IdentityIcon
|
||||
address={ session }
|
||||
tiny
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import RequestOrigin from './';
|
||||
|
||||
const context = {
|
||||
context: {
|
||||
api: {
|
||||
transport: {
|
||||
sessionHash: '1234'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
describe('views/Signer/components/RequestOrigin', () => {
|
||||
it('renders unknown', () => {
|
||||
expect(shallow(
|
||||
<RequestOrigin origin={ { type: 'unknown', details: '' } } />,
|
||||
context
|
||||
).find('FormattedMessage').props().id).to.equal('signer.requestOrigin.unknownInterface');
|
||||
});
|
||||
|
||||
it('renders dapps', () => {
|
||||
expect(shallow(
|
||||
<RequestOrigin origin={ { type: 'dapp', details: 'http://parity.io' } } />,
|
||||
context
|
||||
).find('FormattedMessage').props().id).to.equal('signer.requestOrigin.dapp');
|
||||
});
|
||||
|
||||
it('renders rpc', () => {
|
||||
expect(shallow(
|
||||
<RequestOrigin origin={ { type: 'rpc', details: '' } } />,
|
||||
context
|
||||
).find('FormattedMessage').props().id).to.equal('signer.requestOrigin.rpc');
|
||||
});
|
||||
|
||||
it('renders ipc', () => {
|
||||
expect(shallow(
|
||||
<RequestOrigin origin={ { type: 'ipc', details: '0x1234' } } />,
|
||||
context
|
||||
).find('FormattedMessage').props().id).to.equal('signer.requestOrigin.ipc');
|
||||
});
|
||||
|
||||
it('renders signer', () => {
|
||||
expect(shallow(
|
||||
<RequestOrigin origin={ { type: 'signer', details: '0x12345' } } />,
|
||||
context
|
||||
).find('FormattedMessage').props().id).to.equal('signer.requestOrigin.signerUI');
|
||||
|
||||
expect(shallow(
|
||||
<RequestOrigin origin={ { type: 'signer', details: '0x1234' } } />,
|
||||
context
|
||||
).find('FormattedMessage').props().id).to.equal('signer.requestOrigin.signerCurrent');
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './requestPending';
|
||||
@@ -1,126 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import DecryptRequest from '../DecryptRequest';
|
||||
import SignRequest from '../SignRequest';
|
||||
import TransactionPending from '../TransactionPending';
|
||||
|
||||
export default class RequestPending extends Component {
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
date: PropTypes.instanceOf(Date).isRequired,
|
||||
focus: PropTypes.bool,
|
||||
gasLimit: PropTypes.object.isRequired,
|
||||
id: PropTypes.object.isRequired,
|
||||
isSending: PropTypes.bool.isRequired,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
onReject: PropTypes.func.isRequired,
|
||||
origin: PropTypes.object.isRequired,
|
||||
payload: PropTypes.oneOfType([
|
||||
PropTypes.shape({ decrypt: PropTypes.object.isRequired }),
|
||||
PropTypes.shape({ sendTransaction: PropTypes.object.isRequired }),
|
||||
PropTypes.shape({ sign: PropTypes.object.isRequired }),
|
||||
PropTypes.shape({ signTransaction: PropTypes.object.isRequired })
|
||||
]).isRequired,
|
||||
signerStore: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
focus: false,
|
||||
isSending: false
|
||||
};
|
||||
|
||||
render () {
|
||||
const { className, date, focus, gasLimit, id, isSending, netVersion, onReject, payload, signerStore, origin } = this.props;
|
||||
|
||||
if (payload.sign) {
|
||||
const { sign } = payload;
|
||||
|
||||
return (
|
||||
<SignRequest
|
||||
address={ sign.address }
|
||||
className={ className }
|
||||
focus={ focus }
|
||||
data={ sign.data }
|
||||
id={ id }
|
||||
isFinished={ false }
|
||||
isSending={ isSending }
|
||||
netVersion={ netVersion }
|
||||
onConfirm={ this.onConfirm }
|
||||
onReject={ onReject }
|
||||
origin={ origin }
|
||||
signerStore={ signerStore }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (payload.decrypt) {
|
||||
const { decrypt } = payload;
|
||||
|
||||
return (
|
||||
<DecryptRequest
|
||||
address={ decrypt.address }
|
||||
className={ className }
|
||||
focus={ focus }
|
||||
data={ decrypt.msg }
|
||||
id={ id }
|
||||
isFinished={ false }
|
||||
isSending={ isSending }
|
||||
netVersion={ netVersion }
|
||||
onConfirm={ this.onConfirm }
|
||||
onReject={ onReject }
|
||||
origin={ origin }
|
||||
signerStore={ signerStore }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const transaction = payload.sendTransaction || payload.signTransaction;
|
||||
|
||||
if (transaction) {
|
||||
return (
|
||||
<TransactionPending
|
||||
className={ className }
|
||||
date={ date }
|
||||
focus={ focus }
|
||||
gasLimit={ gasLimit }
|
||||
id={ id }
|
||||
isSending={ isSending }
|
||||
netVersion={ netVersion }
|
||||
onConfirm={ this.onConfirm }
|
||||
onReject={ onReject }
|
||||
origin={ origin }
|
||||
signerStore={ signerStore }
|
||||
transaction={ transaction }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
console.error('RequestPending: Unknown payload', payload);
|
||||
return null;
|
||||
}
|
||||
|
||||
onConfirm = (data) => {
|
||||
const { onConfirm, payload } = this.props;
|
||||
|
||||
data.payload = payload;
|
||||
onConfirm(data);
|
||||
};
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import RequestPending from './';
|
||||
|
||||
const ADDRESS = '0x1234567890123456789012345678901234567890';
|
||||
const TRANSACTION = {
|
||||
from: ADDRESS,
|
||||
gas: new BigNumber(21000),
|
||||
gasPrice: new BigNumber(20000000),
|
||||
value: new BigNumber(1)
|
||||
};
|
||||
const PAYLOAD_SENDTX = {
|
||||
sendTransaction: TRANSACTION
|
||||
};
|
||||
const PAYLOAD_SIGN = {
|
||||
sign: {
|
||||
address: ADDRESS,
|
||||
data: 'testing'
|
||||
}
|
||||
};
|
||||
const PAYLOAD_SIGNTX = {
|
||||
signTransaction: TRANSACTION
|
||||
};
|
||||
const PAYLOAD_DECRYPT = {
|
||||
decrypt: {
|
||||
address: ADDRESS,
|
||||
msg: 'testing'
|
||||
}
|
||||
};
|
||||
|
||||
let component;
|
||||
let onConfirm;
|
||||
let onReject;
|
||||
|
||||
function render (payload) {
|
||||
onConfirm = sinon.stub();
|
||||
onReject = sinon.stub();
|
||||
|
||||
component = shallow(
|
||||
<RequestPending
|
||||
date={ new Date() }
|
||||
gasLimit={ new BigNumber(100000) }
|
||||
id={ new BigNumber(123) }
|
||||
isSending={ false }
|
||||
netVersion='42'
|
||||
onConfirm={ onConfirm }
|
||||
onReject={ onReject }
|
||||
origin={ {} }
|
||||
payload={ payload }
|
||||
store={ {} }
|
||||
/>
|
||||
);
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('views/Signer/RequestPending', () => {
|
||||
describe('sendTransaction', () => {
|
||||
beforeEach(() => {
|
||||
render(PAYLOAD_SENDTX);
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders TransactionPending component', () => {
|
||||
expect(component.find('Connect(TransactionPending)')).to.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sign', () => {
|
||||
beforeEach(() => {
|
||||
render(PAYLOAD_SIGN);
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders SignRequest component', () => {
|
||||
expect(component.find('Connect(SignRequest)')).to.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('signTransaction', () => {
|
||||
beforeEach(() => {
|
||||
render(PAYLOAD_SIGNTX);
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders TransactionPending component', () => {
|
||||
expect(component.find('Connect(TransactionPending)')).to.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('decrypt', () => {
|
||||
beforeEach(() => {
|
||||
render(PAYLOAD_DECRYPT);
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders DecryptRequest component', () => {
|
||||
expect(component.find('Connect(DecryptRequest)')).to.have.length(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './signRequest';
|
||||
@@ -1,91 +0,0 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../../_layout.css';
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
padding: 1.5em 1em 1.5em 0;
|
||||
}
|
||||
|
||||
.actions, .signDetails {
|
||||
vertical-align: middle;
|
||||
min-height: $pendingHeight;
|
||||
}
|
||||
|
||||
.signData {
|
||||
border: 0.25em solid red;
|
||||
margin-left: 2em;
|
||||
padding: 0.5em;
|
||||
overflow: auto;
|
||||
max-height: 6em;
|
||||
max-width: calc(100% - 2em);
|
||||
}
|
||||
|
||||
.signData > p {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.signDetails {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.account img {
|
||||
display: inline-block;
|
||||
height: 50px;
|
||||
margin: 5px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.address, .info {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.address {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: #E53935;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.info p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* TODO [todr] copy&paste from transactions */
|
||||
.isConfirmed {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.isRejected {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.txHash {
|
||||
display: block;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: inline-block;
|
||||
min-height: $finishedHeight;
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import HardwareStore from '@parity/shared/mobx/hardwareStore';
|
||||
|
||||
import Account from '../Account';
|
||||
import TransactionPendingForm from '../TransactionPendingForm';
|
||||
import RequestOrigin from '../RequestOrigin';
|
||||
|
||||
import styles from './signRequest.css';
|
||||
|
||||
function isAscii (data) {
|
||||
for (var i = 2; i < data.length; i += 2) {
|
||||
let n = parseInt(data.substr(i, 2), 16);
|
||||
|
||||
if (n < 32 || n >= 128) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@observer
|
||||
class SignRequest extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
address: PropTypes.string.isRequired,
|
||||
data: PropTypes.string.isRequired,
|
||||
id: PropTypes.object.isRequired,
|
||||
isFinished: PropTypes.bool.isRequired,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
signerStore: PropTypes.object.isRequired,
|
||||
|
||||
className: PropTypes.string,
|
||||
focus: PropTypes.bool,
|
||||
isSending: PropTypes.bool,
|
||||
onConfirm: PropTypes.func,
|
||||
onReject: PropTypes.func,
|
||||
origin: PropTypes.any,
|
||||
status: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
focus: false,
|
||||
origin: {
|
||||
type: 'unknown',
|
||||
details: ''
|
||||
}
|
||||
};
|
||||
|
||||
hardwareStore = HardwareStore.get(this.context.api);
|
||||
|
||||
componentWillMount () {
|
||||
const { address, signerStore } = this.props;
|
||||
|
||||
signerStore.fetchBalance(address);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ `${styles.container} ${className}` }>
|
||||
{ this.renderDetails() }
|
||||
{ this.renderActions() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAsciiDetails (ascii) {
|
||||
return (
|
||||
<div className={ styles.signData }>
|
||||
<p>{ascii}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderBinaryDetails (data) {
|
||||
return (
|
||||
<div className={ styles.signData }>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='signer.signRequest.unknownBinary'
|
||||
defaultMessage='(Unknown binary data)'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderDetails () {
|
||||
const { api } = this.context;
|
||||
const { address, data, netVersion, origin, signerStore } = this.props;
|
||||
const { balances, externalLink } = signerStore;
|
||||
|
||||
const balance = balances[address];
|
||||
|
||||
if (!balance) {
|
||||
return <div />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.signDetails }>
|
||||
<div className={ styles.address }>
|
||||
<Account
|
||||
address={ address }
|
||||
balance={ balance }
|
||||
className={ styles.account }
|
||||
externalLink={ externalLink }
|
||||
netVersion={ netVersion }
|
||||
/>
|
||||
<RequestOrigin origin={ origin } />
|
||||
</div>
|
||||
<div className={ styles.info } title={ api.util.sha3(data) }>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='signer.signRequest.request'
|
||||
defaultMessage='A request to sign data using your account:'
|
||||
/>
|
||||
</p>
|
||||
{
|
||||
isAscii(data)
|
||||
? this.renderAsciiDetails(api.util.hexToAscii(data))
|
||||
: this.renderBinaryDetails(data)
|
||||
}
|
||||
<p>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='signer.signRequest.warning'
|
||||
defaultMessage='WARNING: This consequences of doing this may be grave. Confirm the request only if you are sure.'
|
||||
/>
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderActions () {
|
||||
const { accounts, address, focus, isFinished, status, data } = this.props;
|
||||
const account = accounts[address] || {};
|
||||
const disabled = account.hardware && !this.hardwareStore.isConnected(address);
|
||||
|
||||
if (isFinished) {
|
||||
if (status === 'confirmed') {
|
||||
return (
|
||||
<div className={ styles.actions }>
|
||||
<span className={ styles.isConfirmed }>
|
||||
<FormattedMessage
|
||||
id='signer.signRequest.state.confirmed'
|
||||
defaultMessage='Confirmed'
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.actions }>
|
||||
<span className={ styles.isRejected }>
|
||||
<FormattedMessage
|
||||
id='signer.signRequest.state.rejected'
|
||||
defaultMessage='Rejected'
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TransactionPendingForm
|
||||
account={ account }
|
||||
address={ address }
|
||||
disabled={ disabled }
|
||||
focus={ focus }
|
||||
isSending={ this.props.isSending }
|
||||
netVersion={ this.props.netVersion }
|
||||
onConfirm={ this.onConfirm }
|
||||
onReject={ this.onReject }
|
||||
className={ styles.actions }
|
||||
dataToSign={ { data } }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
onConfirm = (data) => {
|
||||
const { id } = this.props;
|
||||
const { password, dataSigned, wallet } = data;
|
||||
|
||||
this.props.onConfirm({ id, password, dataSigned, wallet });
|
||||
}
|
||||
|
||||
onReject = () => {
|
||||
this.props.onReject(this.props.id);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { accounts } = state.personal;
|
||||
|
||||
return {
|
||||
accounts
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(SignRequest);
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import SignRequest from './';
|
||||
|
||||
let component;
|
||||
let reduxStore;
|
||||
let signerStore;
|
||||
|
||||
function createSignerStore () {
|
||||
return {
|
||||
balances: {},
|
||||
fetchBalance: sinon.stub()
|
||||
};
|
||||
}
|
||||
|
||||
function createReduxStore () {
|
||||
return {
|
||||
dispatch: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
getState: () => {
|
||||
return {
|
||||
personal: {
|
||||
accounts: {}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function render () {
|
||||
reduxStore = createReduxStore();
|
||||
signerStore = createSignerStore();
|
||||
|
||||
component = shallow(
|
||||
<SignRequest signerStore={ signerStore } />,
|
||||
{
|
||||
context: {
|
||||
store: reduxStore
|
||||
}
|
||||
}
|
||||
).find('SignRequest').shallow();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('views/Signer/components/SignRequest', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './transactionMainDetails';
|
||||
@@ -1,80 +0,0 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../../_layout.css';
|
||||
|
||||
.account {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contractIcon {
|
||||
background: #eee;
|
||||
width: 50px !important;
|
||||
height: 50px !important;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
padding: 13px;
|
||||
}
|
||||
|
||||
.editButtonRow {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.from {
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
vertical-align: top;
|
||||
|
||||
.account {
|
||||
img {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.method {
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
vertical-align: top;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.tx {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
margin: 0 -75px;
|
||||
width: 150px;
|
||||
top: -20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.total {
|
||||
font-size: 0.6em;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.transaction {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
||||
import { Button, MethodDecoding } from '@parity/ui';
|
||||
import { GasIcon } from '@parity/ui/Icons';
|
||||
|
||||
import * as tUtil from '../util/transaction';
|
||||
import Account from '../Account';
|
||||
import RequestOrigin from '../RequestOrigin';
|
||||
|
||||
import styles from './transactionMainDetails.css';
|
||||
|
||||
export default class TransactionMainDetails extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
disabled: PropTypes.bool,
|
||||
externalLink: PropTypes.string.isRequired,
|
||||
from: PropTypes.string.isRequired,
|
||||
fromBalance: PropTypes.object,
|
||||
gasStore: PropTypes.object,
|
||||
id: PropTypes.object.isRequired,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
origin: PropTypes.any,
|
||||
totalValue: PropTypes.object.isRequired,
|
||||
transaction: PropTypes.object.isRequired,
|
||||
value: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
origin: {
|
||||
type: 'unknown',
|
||||
details: ''
|
||||
}
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
const { totalValue, value } = this.props;
|
||||
|
||||
this.updateDisplayValues(value, totalValue);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
const { totalValue, value } = nextProps;
|
||||
|
||||
this.updateDisplayValues(value, totalValue);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children, disabled, externalLink, from, fromBalance, gasStore, netVersion, transaction, origin } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ styles.transaction }>
|
||||
<div className={ styles.from }>
|
||||
<div className={ styles.account }>
|
||||
<Account
|
||||
address={ from }
|
||||
balance={ fromBalance }
|
||||
disabled={ disabled }
|
||||
externalLink={ externalLink }
|
||||
netVersion={ netVersion }
|
||||
/>
|
||||
</div>
|
||||
<RequestOrigin origin={ origin } />
|
||||
</div>
|
||||
<div className={ styles.method }>
|
||||
<MethodDecoding
|
||||
address={ from }
|
||||
historic={ false }
|
||||
transaction={
|
||||
gasStore
|
||||
? gasStore.overrideTransaction(transaction)
|
||||
: transaction
|
||||
}
|
||||
/>
|
||||
{ this.renderEditTx() }
|
||||
</div>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderEditTx () {
|
||||
const { gasStore } = this.props;
|
||||
|
||||
if (!gasStore) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.editButtonRow }>
|
||||
<Button
|
||||
icon={ <GasIcon /> }
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='signer.mainDetails.editTx'
|
||||
defaultMessage='Edit conditions/gas/gasPrice'
|
||||
/>
|
||||
}
|
||||
onClick={ this.toggleGasEditor }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTotalValue () {
|
||||
const { id } = this.props;
|
||||
const { feeEth, totalValueDisplay, totalValueDisplayWei } = this.state;
|
||||
const labelId = `totalValue${id}`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className={ styles.total }
|
||||
data-effect='solid'
|
||||
data-for={ labelId }
|
||||
data-place='bottom'
|
||||
data-tip
|
||||
>
|
||||
{ totalValueDisplay } <small>ETH</small>
|
||||
</div>
|
||||
<ReactTooltip id={ labelId }>
|
||||
<FormattedMessage
|
||||
id='signer.mainDetails.tooltips.total1'
|
||||
defaultMessage='The value of the transaction including the mining fee is {total} {type}.'
|
||||
values={ {
|
||||
total: <strong>{ totalValueDisplayWei }</strong>,
|
||||
type: <small>WEI</small>
|
||||
} }
|
||||
/>
|
||||
<br />
|
||||
<FormattedMessage
|
||||
id='signer.mainDetails.tooltips.total2'
|
||||
defaultMessage='(This includes a mining fee of {fee} {token})'
|
||||
values={ {
|
||||
fee: <strong>{ feeEth }</strong>,
|
||||
token: <small>ETH</small>
|
||||
} }
|
||||
/>
|
||||
</ReactTooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderValue () {
|
||||
const { id } = this.props;
|
||||
const { valueDisplay, valueDisplayWei } = this.state;
|
||||
const labelId = `value${id}`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
data-effect='solid'
|
||||
data-for={ labelId }
|
||||
data-tip
|
||||
>
|
||||
<strong>{ valueDisplay } </strong>
|
||||
<small>ETH</small>
|
||||
</div>
|
||||
<ReactTooltip id={ labelId }>
|
||||
<FormattedMessage
|
||||
id='signer.mainDetails.tooltips.value1'
|
||||
defaultMessage='The value of the transaction.'
|
||||
/>
|
||||
<br />
|
||||
<strong>{ valueDisplayWei }</strong> <small>WEI</small>
|
||||
</ReactTooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
updateDisplayValues (value, totalValue) {
|
||||
this.setState({
|
||||
feeEth: tUtil.calcFeeInEth(totalValue, value),
|
||||
totalValueDisplay: tUtil.getTotalValueDisplay(totalValue),
|
||||
totalValueDisplayWei: tUtil.getTotalValueDisplayWei(totalValue),
|
||||
valueDisplay: tUtil.getValueDisplay(value),
|
||||
valueDisplayWei: tUtil.getValueDisplayWei(value)
|
||||
});
|
||||
}
|
||||
|
||||
toggleGasEditor = () => {
|
||||
this.props.gasStore.setEditing(true);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './transactionPending';
|
||||
@@ -1,27 +0,0 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../../_layout.css';
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
padding: 1.5em 1em 1.5em 0;
|
||||
|
||||
& > * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import HardwareStore from '@parity/shared/mobx/hardwareStore';
|
||||
import { Button, GasPriceEditor } from '@parity/ui';
|
||||
|
||||
import TransactionMainDetails from '../TransactionMainDetails';
|
||||
import TransactionPendingForm from '../TransactionPendingForm';
|
||||
|
||||
import styles from './transactionPending.css';
|
||||
|
||||
import * as tUtil from '../util/transaction';
|
||||
|
||||
@observer
|
||||
class TransactionPending extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
className: PropTypes.string,
|
||||
date: PropTypes.instanceOf(Date).isRequired,
|
||||
focus: PropTypes.bool,
|
||||
gasLimit: PropTypes.object,
|
||||
id: PropTypes.object.isRequired,
|
||||
isSending: PropTypes.bool.isRequired,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
nonce: PropTypes.number,
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
onReject: PropTypes.func.isRequired,
|
||||
origin: PropTypes.any,
|
||||
signerStore: PropTypes.object.isRequired,
|
||||
transaction: PropTypes.shape({
|
||||
condition: PropTypes.object,
|
||||
data: PropTypes.string,
|
||||
from: PropTypes.string.isRequired,
|
||||
gas: PropTypes.object.isRequired,
|
||||
gasPrice: PropTypes.object.isRequired,
|
||||
to: PropTypes.string,
|
||||
value: PropTypes.object.isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
focus: false,
|
||||
origin: {
|
||||
type: 'unknown',
|
||||
details: ''
|
||||
}
|
||||
};
|
||||
|
||||
gasStore = new GasPriceEditor.Store(this.context.api, {
|
||||
condition: this.props.transaction.condition,
|
||||
gas: this.props.transaction.gas.toFixed(),
|
||||
gasLimit: this.props.gasLimit,
|
||||
gasPrice: this.props.transaction.gasPrice.toFixed()
|
||||
});
|
||||
|
||||
hardwareStore = HardwareStore.get(this.context.api);
|
||||
|
||||
componentWillMount () {
|
||||
const { signerStore, transaction } = this.props;
|
||||
const { from, gas, gasPrice, to, value } = transaction;
|
||||
|
||||
const fee = tUtil.getFee(gas, gasPrice); // BigNumber object
|
||||
const gasPriceEthmDisplay = tUtil.getEthmFromWeiDisplay(gasPrice);
|
||||
const gasToDisplay = tUtil.getGasDisplay(gas);
|
||||
const totalValue = tUtil.getTotalValue(fee, value);
|
||||
|
||||
this.setState({ gasPriceEthmDisplay, totalValue, gasToDisplay });
|
||||
this.gasStore.setEthValue(value);
|
||||
signerStore.fetchBalances([from, to]);
|
||||
}
|
||||
|
||||
render () {
|
||||
return this.gasStore.isEditing
|
||||
? this.renderTxEditor()
|
||||
: this.renderTransaction();
|
||||
}
|
||||
|
||||
renderTransaction () {
|
||||
const transaction = this.gasStore.overrideTransaction(this.props.transaction);
|
||||
|
||||
const { accounts, className, focus, id, isSending, netVersion, origin, signerStore } = this.props;
|
||||
const { totalValue } = this.state;
|
||||
const { balances, externalLink } = signerStore;
|
||||
const { from, value } = transaction;
|
||||
const fromBalance = balances[from];
|
||||
const account = accounts[from] || {};
|
||||
const disabled = account.hardware && !this.hardwareStore.isConnected(from);
|
||||
|
||||
return (
|
||||
<div className={ `${styles.container} ${className}` }>
|
||||
<TransactionMainDetails
|
||||
className={ styles.transactionDetails }
|
||||
disabled={ disabled }
|
||||
externalLink={ externalLink }
|
||||
from={ from }
|
||||
fromBalance={ fromBalance }
|
||||
gasStore={ this.gasStore }
|
||||
id={ id }
|
||||
netVersion={ netVersion }
|
||||
origin={ origin }
|
||||
totalValue={ totalValue }
|
||||
transaction={ transaction }
|
||||
value={ value }
|
||||
/>
|
||||
<TransactionPendingForm
|
||||
account={ account }
|
||||
address={ from }
|
||||
disabled={ disabled }
|
||||
focus={ focus }
|
||||
isSending={ isSending }
|
||||
netVersion={ netVersion }
|
||||
onConfirm={ this.onConfirm }
|
||||
onReject={ this.onReject }
|
||||
dataToSign={ { transaction } }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTxEditor () {
|
||||
const { className } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ `${styles.container} ${className}` }>
|
||||
<GasPriceEditor store={ this.gasStore }>
|
||||
<Button
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='signer.txPending.buttons.viewToggle'
|
||||
defaultMessage='view transaction'
|
||||
/>
|
||||
}
|
||||
onClick={ this.toggleGasEditor }
|
||||
/>
|
||||
</GasPriceEditor>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onConfirm = (data) => {
|
||||
const { id, transaction } = this.props;
|
||||
const { password, txSigned, wallet } = data;
|
||||
const { condition, gas, gasPrice } = this.gasStore.overrideTransaction(transaction);
|
||||
|
||||
const options = {
|
||||
gas,
|
||||
gasPrice,
|
||||
id,
|
||||
password,
|
||||
txSigned,
|
||||
wallet
|
||||
};
|
||||
|
||||
if (condition && (condition.block || condition.time)) {
|
||||
options.condition = condition;
|
||||
}
|
||||
|
||||
this.props.onConfirm(options);
|
||||
}
|
||||
|
||||
onReject = () => {
|
||||
this.props.onReject(this.props.id);
|
||||
}
|
||||
|
||||
toggleGasEditor = () => {
|
||||
this.gasStore.setEditing(false);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { accounts } = state.personal;
|
||||
|
||||
return {
|
||||
accounts
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(TransactionPending);
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './transactionPendingForm';
|
||||
@@ -1,44 +0,0 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../../_layout.css';
|
||||
|
||||
.container {
|
||||
box-sizing: border-box;
|
||||
padding: 1em 0 0 2em;
|
||||
flex: 0 0 $statusWidth;
|
||||
}
|
||||
|
||||
.rejectToggle {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
color: #00e;
|
||||
opacity: .7;
|
||||
transition: opacity .5s;
|
||||
}
|
||||
|
||||
.rejectToggle:hover {
|
||||
opacity: 1;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.rejectToggle svg {
|
||||
position: relative;
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
top: 3px;
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { PrevIcon } from '@parity/ui/Icons';
|
||||
|
||||
import TransactionPendingFormConfirm from '../TransactionPendingFormConfirm';
|
||||
import TransactionPendingFormReject from '../TransactionPendingFormReject';
|
||||
|
||||
import styles from './transactionPendingForm.css';
|
||||
|
||||
export default class TransactionPendingForm extends Component {
|
||||
static propTypes = {
|
||||
account: PropTypes.object,
|
||||
address: PropTypes.string.isRequired,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
focus: PropTypes.bool,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
isSending: PropTypes.bool.isRequired,
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
onReject: PropTypes.func.isRequired,
|
||||
dataToSign: PropTypes.oneOfType([
|
||||
PropTypes.shape({
|
||||
transaction: PropTypes.object.isRequired
|
||||
}),
|
||||
PropTypes.shape({
|
||||
data: PropTypes.string.isRequired
|
||||
}),
|
||||
PropTypes.shape({
|
||||
decrypt: PropTypes.string.isRequired
|
||||
})
|
||||
]).isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
account: {},
|
||||
focus: false
|
||||
};
|
||||
|
||||
state = {
|
||||
isRejectOpen: false
|
||||
};
|
||||
|
||||
render () {
|
||||
const { className } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ `${styles.container} ${className}` }>
|
||||
{ this.renderForm() }
|
||||
{ this.renderRejectToggle() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderForm () {
|
||||
const { account, address, disabled, focus, isSending, netVersion, onConfirm, onReject, dataToSign } = this.props;
|
||||
|
||||
if (this.state.isRejectOpen) {
|
||||
return (
|
||||
<TransactionPendingFormReject onReject={ onReject } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TransactionPendingFormConfirm
|
||||
address={ address }
|
||||
account={ account }
|
||||
disabled={ disabled }
|
||||
focus={ focus }
|
||||
netVersion={ netVersion }
|
||||
isSending={ isSending }
|
||||
onConfirm={ onConfirm }
|
||||
dataToSign={ dataToSign }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderRejectToggle () {
|
||||
const { isRejectOpen } = this.state;
|
||||
let html;
|
||||
|
||||
if (!isRejectOpen) {
|
||||
html = (
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='signer.txPendingForm.reject'
|
||||
defaultMessage='reject request'
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
html = (
|
||||
<span>
|
||||
<PrevIcon />
|
||||
<FormattedMessage
|
||||
id='signer.txPendingForm.changedMind'
|
||||
defaultMessage="I've changed my mind"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
className={ styles.rejectToggle }
|
||||
onClick={ this.onToggleReject }
|
||||
>
|
||||
{ html }
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
onToggleReject = () => {
|
||||
const { isRejectOpen } = this.state;
|
||||
|
||||
this.setState({
|
||||
isRejectOpen: !isRejectOpen
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './transactionPendingFormConfirm';
|
||||
@@ -1,48 +0,0 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.confirmForm {
|
||||
margin-top: -2em;
|
||||
}
|
||||
|
||||
.confirmButton {
|
||||
display: block !important;
|
||||
margin-bottom: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.signerIcon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.passwordHint {
|
||||
font-size: 0.75em;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
margin-bottom: 0.75em;
|
||||
}
|
||||
|
||||
.passwordHint span {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.fileInput input {
|
||||
top: 22px;
|
||||
}
|
||||
|
||||
.qr {
|
||||
margin-bottom: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -1,580 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import keycode from 'keycode';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
||||
import { generateTxQr, generateDecryptQr, generateDataQr } from '@parity/shared/util/qrscan';
|
||||
import { Button, Form, Input, IdentityIcon, QrCode, QrScan } from '@parity/ui';
|
||||
|
||||
import styles from './transactionPendingFormConfirm.css';
|
||||
|
||||
const QR_VISIBLE = 1;
|
||||
const QR_SCAN = 2;
|
||||
const QR_COMPLETED = 3;
|
||||
|
||||
export default class TransactionPendingFormConfirm extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
account: PropTypes.object,
|
||||
address: PropTypes.string.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
focus: PropTypes.bool,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
isSending: PropTypes.bool.isRequired,
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
dataToSign: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
account: {},
|
||||
focus: false
|
||||
};
|
||||
|
||||
id = Math.random(); // for tooltip
|
||||
|
||||
state = {
|
||||
password: '',
|
||||
qrState: QR_VISIBLE,
|
||||
qr: {},
|
||||
wallet: null,
|
||||
walletError: null
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.readNonce();
|
||||
this.subscribeNonce();
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.unsubscribeNonce();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (!this.props.focus && nextProps.focus) {
|
||||
this.focus(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
focus (props = this.props) {
|
||||
if (props.focus) {
|
||||
const textNode = ReactDOM.findDOMNode(this.refs.input);
|
||||
|
||||
if (!textNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inputNode = textNode.querySelector('input');
|
||||
|
||||
inputNode && inputNode.focus();
|
||||
}
|
||||
}
|
||||
|
||||
getPasswordHint () {
|
||||
const { account } = this.props;
|
||||
const accountHint = account.meta && account.meta.passwordHint;
|
||||
|
||||
if (accountHint) {
|
||||
return accountHint;
|
||||
}
|
||||
|
||||
const { wallet } = this.state;
|
||||
const walletHint = wallet && wallet.meta && wallet.meta.passwordHint;
|
||||
|
||||
return walletHint || null;
|
||||
}
|
||||
|
||||
// TODO: Now that we have 3 types, it would make sense splitting each into their own
|
||||
// sub-module and having the consistent bits combined (e.g. i18n, layouts)
|
||||
render () {
|
||||
const { account, address, disabled, isSending } = this.props;
|
||||
const { wallet, walletError } = this.state;
|
||||
const isAccount = account.external || account.hardware || account.uuid;
|
||||
const isWalletOk = isAccount || (walletError === null && wallet !== null);
|
||||
const confirmText = this.renderConfirmButton();
|
||||
const confirmButton = confirmText
|
||||
? (
|
||||
<div
|
||||
data-effect='solid'
|
||||
data-for={ `transactionConfirmForm${this.id}` }
|
||||
data-place='bottom'
|
||||
data-tip
|
||||
>
|
||||
<Button
|
||||
className={ styles.confirmButton }
|
||||
disabled={ disabled || isSending || !isWalletOk }
|
||||
fullWidth
|
||||
icon={
|
||||
<IdentityIcon
|
||||
address={ address }
|
||||
button
|
||||
className={ styles.signerIcon }
|
||||
/>
|
||||
}
|
||||
label={ confirmText }
|
||||
onClick={ this.onConfirm }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className={ styles.confirmForm }>
|
||||
<Form>
|
||||
{ this.renderKeyInput() }
|
||||
{ this.renderQrCode() }
|
||||
{ this.renderQrScanner() }
|
||||
{ this.renderPassword() }
|
||||
{ this.renderHint() }
|
||||
{ confirmButton }
|
||||
{ this.renderTooltip() }
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderConfirmButton () {
|
||||
const { account, isSending } = this.props;
|
||||
const { qrState } = this.state;
|
||||
|
||||
if (account.external) {
|
||||
switch (qrState) {
|
||||
case QR_VISIBLE:
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.buttons.scanSigned'
|
||||
defaultMessage='Scan Signed QR'
|
||||
/>
|
||||
);
|
||||
|
||||
case QR_SCAN:
|
||||
case QR_COMPLETED:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return isSending
|
||||
? (
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.buttons.confirmBusy'
|
||||
defaultMessage='Confirming...'
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.buttons.confirmRequest'
|
||||
defaultMessage='Confirm Request'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderPassword () {
|
||||
const { account } = this.props;
|
||||
const { password } = this.state;
|
||||
|
||||
if (account.hardware || account.external) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isAccount = account.uuid;
|
||||
|
||||
return (
|
||||
<Input
|
||||
hint={
|
||||
isAccount
|
||||
? (
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.password.unlock.hint'
|
||||
defaultMessage='unlock the account'
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.password.decrypt.hint'
|
||||
defaultMessage='decrypt the key'
|
||||
/>
|
||||
)
|
||||
}
|
||||
label={
|
||||
isAccount
|
||||
? (
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.password.unlock.label'
|
||||
defaultMessage='Account Password'
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.password.decrypt.label'
|
||||
defaultMessage='Key Password'
|
||||
/>
|
||||
)
|
||||
}
|
||||
onChange={ this.onModifyPassword }
|
||||
onKeyDown={ this.onKeyDown }
|
||||
ref='input'
|
||||
type='password'
|
||||
value={ password }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderHint () {
|
||||
const { account, disabled, isSending } = this.props;
|
||||
const { qrState } = this.state;
|
||||
|
||||
if (account.external) {
|
||||
switch (qrState) {
|
||||
case QR_VISIBLE:
|
||||
return (
|
||||
<div className={ styles.passwordHint }>
|
||||
<FormattedMessage
|
||||
id='signer.sending.external.scanTx'
|
||||
defaultMessage='Please scan the transaction QR on your external device'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
case QR_SCAN:
|
||||
return (
|
||||
<div className={ styles.passwordHint }>
|
||||
<FormattedMessage
|
||||
id='signer.sending.external.scanSigned'
|
||||
defaultMessage='Scan the QR code of the signed transaction from your external device'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
case QR_COMPLETED:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (account.hardware) {
|
||||
if (isSending) {
|
||||
return (
|
||||
<div className={ styles.passwordHint }>
|
||||
<FormattedMessage
|
||||
id='signer.sending.hardware.confirm'
|
||||
defaultMessage='Please confirm the transaction on your attached hardware device'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else if (disabled) {
|
||||
return (
|
||||
<div className={ styles.passwordHint }>
|
||||
<FormattedMessage
|
||||
id='signer.sending.hardware.connect'
|
||||
defaultMessage='Please attach your hardware device before confirming the transaction'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const passwordHint = this.getPasswordHint();
|
||||
|
||||
if (!passwordHint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.passwordHint }>
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.passwordHint'
|
||||
defaultMessage='(hint) {passwordHint}'
|
||||
values={ {
|
||||
passwordHint
|
||||
} }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Split into sub-scomponent
|
||||
renderQrCode () {
|
||||
const { account } = this.props;
|
||||
const { qrState, qr } = this.state;
|
||||
|
||||
if (!account.external || qrState !== QR_VISIBLE || !qr.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<QrCode
|
||||
className={ styles.qr }
|
||||
value={ qr.value }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Split into sub-scomponent
|
||||
renderQrScanner () {
|
||||
const { account } = this.props;
|
||||
const { qrState } = this.state;
|
||||
|
||||
if (!account.external || qrState !== QR_SCAN) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<QrScan
|
||||
className={ styles.camera }
|
||||
onScan={ this.onScan }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderKeyInput () {
|
||||
const { account } = this.props;
|
||||
const { walletError } = this.state;
|
||||
|
||||
if (account.uuid || account.wallet || account.hardware || account.external) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Input
|
||||
className={ styles.fileInput }
|
||||
error={ walletError }
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.selectKey.hint'
|
||||
defaultMessage='The keyfile to use for this account'
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.selectKey.label'
|
||||
defaultMessage='Select Local Key'
|
||||
/>
|
||||
}
|
||||
onChange={ this.onKeySelect }
|
||||
type='file'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderTooltip () {
|
||||
const { account } = this.props;
|
||||
|
||||
if (this.state.password.length || account.hardware || account.external) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ReactTooltip id={ `transactionConfirmForm${this.id}` }>
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.tooltips.password'
|
||||
defaultMessage='Please provide a password for this account'
|
||||
/>
|
||||
</ReactTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
onScan = (signature) => {
|
||||
const { chainId, rlp, tx, data, decrypt } = this.state.qr;
|
||||
|
||||
if (!signature) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (signature && signature.substr(0, 2) !== '0x') {
|
||||
signature = `0x${signature}`;
|
||||
}
|
||||
|
||||
this.setState({ qrState: QR_COMPLETED });
|
||||
|
||||
if (tx) {
|
||||
this.props.onConfirm({
|
||||
txSigned: {
|
||||
chainId,
|
||||
rlp,
|
||||
signature,
|
||||
tx
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (decrypt) {
|
||||
this.props.onConfirm({
|
||||
decrypted: {
|
||||
decrypt,
|
||||
msg: signature
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onConfirm({
|
||||
dataSigned: {
|
||||
data,
|
||||
signature
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onKeySelect = (event) => {
|
||||
// Check that file have been selected
|
||||
if (event.target.files.length === 0) {
|
||||
return this.setState({
|
||||
wallet: null,
|
||||
walletError: null
|
||||
});
|
||||
}
|
||||
|
||||
const fileReader = new FileReader();
|
||||
|
||||
fileReader.onload = (e) => {
|
||||
try {
|
||||
const wallet = JSON.parse(e.target.result);
|
||||
|
||||
try {
|
||||
if (wallet && typeof wallet.meta === 'string') {
|
||||
wallet.meta = JSON.parse(wallet.meta);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
this.setState({
|
||||
wallet,
|
||||
walletError: null
|
||||
});
|
||||
} catch (error) {
|
||||
this.setState({
|
||||
wallet: null,
|
||||
walletError: (
|
||||
<FormattedMessage
|
||||
id='signer.txPendingConfirm.errors.invalidWallet'
|
||||
defaultMessage='Given wallet file is invalid.'
|
||||
/>
|
||||
)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
fileReader.readAsText(event.target.files[0]);
|
||||
}
|
||||
|
||||
onModifyPassword = (event) => {
|
||||
const password = event.target.value;
|
||||
|
||||
this.setState({
|
||||
password
|
||||
});
|
||||
}
|
||||
|
||||
onConfirm = () => {
|
||||
const { account } = this.props;
|
||||
const { password, qrState, wallet } = this.state;
|
||||
|
||||
if (account.external && qrState === QR_VISIBLE) {
|
||||
return this.setState({ qrState: QR_SCAN });
|
||||
}
|
||||
|
||||
this.props.onConfirm({
|
||||
password,
|
||||
wallet
|
||||
});
|
||||
}
|
||||
|
||||
generateQr = () => {
|
||||
const { api } = this.context;
|
||||
const { netVersion, dataToSign } = this.props;
|
||||
const { transaction, data, decrypt } = dataToSign;
|
||||
const setState = qr => {
|
||||
this.setState({ qr });
|
||||
};
|
||||
|
||||
if (transaction) {
|
||||
generateTxQr(api, netVersion, transaction).then(setState);
|
||||
return;
|
||||
}
|
||||
|
||||
if (decrypt) {
|
||||
generateDecryptQr(decrypt).then(setState);
|
||||
return;
|
||||
}
|
||||
|
||||
generateDataQr(data).then(setState);
|
||||
}
|
||||
|
||||
onKeyDown = (event) => {
|
||||
const codeName = keycode(event);
|
||||
|
||||
if (codeName !== 'enter') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onConfirm();
|
||||
}
|
||||
|
||||
// FIXME: Sadly the API subscription channels currently does not allow for specific values,
|
||||
// rather it can only do general queries where parameters are not specified. Hence we are
|
||||
// polling for the nonce here. Since we are moving to node-based subscriptions on the API layer,
|
||||
// this can be optimised when the subscription mechanism is reworked to conform.
|
||||
subscribeNonce () {
|
||||
const nonceTimerId = setInterval(this.readNonce, 1000);
|
||||
|
||||
this.setState({ nonceTimerId });
|
||||
}
|
||||
|
||||
unsubscribeNonce () {
|
||||
const { nonceTimerId } = this.state;
|
||||
|
||||
if (!nonceTimerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(nonceTimerId);
|
||||
}
|
||||
|
||||
readNonce = () => {
|
||||
const { api } = this.context;
|
||||
const { account, dataToSign } = this.props;
|
||||
const { qr } = this.state;
|
||||
|
||||
if ((dataToSign.data || dataToSign.decrypt) && qr && !qr.value) {
|
||||
this.generateQr();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!account || !account.external || !api.transport.isConnected || !dataToSign.transaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
return api.parity
|
||||
.nextNonce(account.address)
|
||||
.then((newNonce) => {
|
||||
const { nonce } = this.state.qr;
|
||||
|
||||
if (!nonce || !newNonce.eq(nonce)) {
|
||||
this.generateQr();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import TransactionPendingFormConfirm from './';
|
||||
|
||||
const ADDR_NORMAL = '0x0123456789012345678901234567890123456789';
|
||||
const ADDR_WALLET = '0x1234567890123456789012345678901234567890';
|
||||
const ADDR_HARDWARE = '0x2345678901234567890123456789012345678901';
|
||||
const ADDR_SIGN = '0x3456789012345678901234567890123456789012';
|
||||
const ACCOUNTS = {
|
||||
[ADDR_NORMAL]: {
|
||||
address: ADDR_NORMAL,
|
||||
uuid: ADDR_NORMAL
|
||||
},
|
||||
[ADDR_WALLET]: {
|
||||
address: ADDR_WALLET,
|
||||
wallet: true
|
||||
},
|
||||
[ADDR_HARDWARE]: {
|
||||
address: ADDR_HARDWARE,
|
||||
hardware: true
|
||||
}
|
||||
};
|
||||
|
||||
let component;
|
||||
let instance;
|
||||
let onConfirm;
|
||||
|
||||
function render (address) {
|
||||
onConfirm = sinon.stub();
|
||||
|
||||
component = shallow(
|
||||
<TransactionPendingFormConfirm
|
||||
account={ ACCOUNTS[address] }
|
||||
address={ address }
|
||||
onConfirm={ onConfirm }
|
||||
isSending={ false }
|
||||
dataToSign={ {} }
|
||||
/>
|
||||
);
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('views/Signer/TransactionPendingFormConfirm', () => {
|
||||
describe('normal accounts', () => {
|
||||
beforeEach(() => {
|
||||
render(ADDR_NORMAL);
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('does not render the key input', () => {
|
||||
expect(instance.renderKeyInput()).to.be.null;
|
||||
});
|
||||
|
||||
it('renders the password', () => {
|
||||
expect(instance.renderPassword()).not.to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
describe('hardware accounts', () => {
|
||||
beforeEach(() => {
|
||||
render(ADDR_HARDWARE);
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('does not render the key input', () => {
|
||||
expect(instance.renderKeyInput()).to.be.null;
|
||||
});
|
||||
|
||||
it('does not render the password', () => {
|
||||
expect(instance.renderPassword()).to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
describe('wallet accounts', () => {
|
||||
beforeEach(() => {
|
||||
render(ADDR_WALLET);
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('does not render the key input', () => {
|
||||
expect(instance.renderKeyInput()).to.be.null;
|
||||
});
|
||||
|
||||
it('renders the password', () => {
|
||||
expect(instance.renderPassword()).not.to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
describe('signing accounts', () => {
|
||||
beforeEach(() => {
|
||||
render(ADDR_SIGN);
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders the key input', () => {
|
||||
expect(instance.renderKeyInput()).not.to.be.null;
|
||||
});
|
||||
|
||||
it('renders the password', () => {
|
||||
expect(instance.renderPassword()).not.to.be.null;
|
||||
});
|
||||
|
||||
it('renders the hint', () => {
|
||||
expect(instance.renderHint()).to.be.null;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './transactionPendingFormReject';
|
||||
@@ -1,26 +0,0 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* the rejection button itself, once .reject has been pressed */
|
||||
.rejectButton {
|
||||
display: block !important;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.rejectText {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Button } from '@parity/ui';
|
||||
|
||||
import styles from './transactionPendingFormReject.css';
|
||||
|
||||
export default class TransactionPendingFormReject extends Component {
|
||||
static propTypes = {
|
||||
onReject: PropTypes.func.isRequired,
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
||||
render () {
|
||||
const { onReject } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={ styles.rejectText }>
|
||||
<FormattedMessage
|
||||
id='signer.txPendingReject.info'
|
||||
defaultMessage='Are you sure you want to reject request?'
|
||||
/>
|
||||
<br />
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='signer.txPendingReject.undone'
|
||||
defaultMessage='This cannot be undone'
|
||||
/>
|
||||
</strong>
|
||||
</div>
|
||||
<Button
|
||||
onClick={ onReject }
|
||||
className={ styles.rejectButton }
|
||||
fullWidth
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='signer.txPendingReject.buttons.reject'
|
||||
defaultMessage='Reject Request'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './txHashLink';
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { txLink } from '@parity/etherscan/links';
|
||||
|
||||
export default class TxHashLink extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
netVersion: PropTypes.string.isRequired,
|
||||
txHash: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children, className, netVersion, txHash } = this.props;
|
||||
|
||||
return (
|
||||
<a
|
||||
className={ className }
|
||||
href={ txLink(txHash, false, netVersion) }
|
||||
target='_blank'
|
||||
>
|
||||
{ children || txHash }
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
const isLogging = process.env.LOGGING;
|
||||
|
||||
export default logger();
|
||||
|
||||
function logger () {
|
||||
return isLogging ? devLogger() : prodLogger();
|
||||
}
|
||||
|
||||
function prodLogger () {
|
||||
return {
|
||||
log: noop,
|
||||
info: noop,
|
||||
error: noop,
|
||||
warn: noop
|
||||
};
|
||||
}
|
||||
|
||||
function devLogger () {
|
||||
return console;
|
||||
}
|
||||
|
||||
function noop () {}
|
||||
21
js/src/Signer/components/util/react.js
vendored
21
js/src/Signer/components/util/react.js
vendored
@@ -1,21 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { isValidElement } from 'react';
|
||||
|
||||
export function isReactComponent (componentOrElem) {
|
||||
return isValidElement(componentOrElem) && typeof componentOrElem.type === 'function';
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
const WEI_TO_ETH_MULTIPLIER = 0.000000000000000001;
|
||||
const WEI_TO_SZABU_MULTIPLIER = 0.000000000001;
|
||||
|
||||
export const getShortData = _getShortData;
|
||||
// calculations
|
||||
export const getFee = _getFee;
|
||||
export const calcFeeInEth = _calcFeeInEth;
|
||||
export const getTotalValue = _getTotalValue;
|
||||
// displays
|
||||
export const getSzaboFromWeiDisplay = _getSzaboFromWeiDisplay;
|
||||
export const getValueDisplay = _getValueDisplay;
|
||||
export const getValueDisplayWei = _getValueDisplayWei;
|
||||
export const getTotalValueDisplay = _getTotalValueDisplay;
|
||||
export const getTotalValueDisplayWei = _getTotalValueDisplayWei;
|
||||
export const getEthmFromWeiDisplay = _getEthmFromWeiDisplay;
|
||||
export const getGasDisplay = _getGasDisplay;
|
||||
|
||||
function _getShortData (data) {
|
||||
if (data.length <= 3) {
|
||||
return data;
|
||||
}
|
||||
return data.substr(0, 3) + '...';
|
||||
}
|
||||
|
||||
/*
|
||||
* @param {hex string} gas
|
||||
* @param {wei hex string} gasPrice
|
||||
* @return {BigNumber} fee in wei
|
||||
*/
|
||||
function _getFee (gas, gasPrice) {
|
||||
gas = new BigNumber(gas);
|
||||
gasPrice = new BigNumber(gasPrice);
|
||||
return gasPrice.times(gas);
|
||||
}
|
||||
|
||||
function _calcFeeInEth (totalValue, value) {
|
||||
let fee = new BigNumber(totalValue).sub(new BigNumber(value));
|
||||
|
||||
return fee.times(WEI_TO_ETH_MULTIPLIER).toFormat(7);
|
||||
}
|
||||
|
||||
/*
|
||||
* @param {wei BigNumber} fee
|
||||
* @param {wei hex string} value
|
||||
* @return {BigNumber} total value in wei
|
||||
*/
|
||||
function _getTotalValue (fee, value) {
|
||||
value = new BigNumber(value);
|
||||
return fee.plus(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* @param {wei hex string} gasPrice
|
||||
* @return {string} szabo gas price with unit [szabo] i.e. 21,423 [szabo]
|
||||
*/
|
||||
function _getSzaboFromWeiDisplay (gasPrice) {
|
||||
gasPrice = new BigNumber(gasPrice);
|
||||
return gasPrice.times(WEI_TO_SZABU_MULTIPLIER).toPrecision(5);
|
||||
}
|
||||
|
||||
/*
|
||||
* @param {wei hex string} value
|
||||
* @return {string} value in WEI nicely formatted
|
||||
*/
|
||||
function _getValueDisplay (value) {
|
||||
value = new BigNumber(value);
|
||||
return value.times(WEI_TO_ETH_MULTIPLIER).toFormat(5);
|
||||
}
|
||||
|
||||
function _getValueDisplayWei (value) {
|
||||
value = new BigNumber(value);
|
||||
return value.toFormat(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* @param {wei hex string} totalValue
|
||||
* @return {string} total value (including fee) with units i.e. 1.32 [eth]
|
||||
*/
|
||||
function _getTotalValueDisplay (totalValue) {
|
||||
totalValue = new BigNumber(totalValue);
|
||||
return totalValue.times(WEI_TO_ETH_MULTIPLIER).toFormat(5);
|
||||
}
|
||||
|
||||
function _getTotalValueDisplayWei (totalValue) {
|
||||
totalValue = new BigNumber(totalValue);
|
||||
return totalValue.toFormat(0);
|
||||
}
|
||||
|
||||
function _getEthmFromWeiDisplay (weiHexString) {
|
||||
const value = new BigNumber(weiHexString);
|
||||
|
||||
return value.times(WEI_TO_ETH_MULTIPLIER).times(1e7).toFixed(5);
|
||||
}
|
||||
|
||||
function _getGasDisplay (gas) {
|
||||
return new BigNumber(gas).times(1e-7).toFormat(4);
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { getShortData, getFee, getTotalValue } from './transaction';
|
||||
|
||||
describe('Signer/components/util/transaction', () => {
|
||||
describe('getEstimatedMiningTime', () => {
|
||||
it('should return estimated mining time', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getShortData', () => {
|
||||
it('should return short data', () => {
|
||||
// given
|
||||
const data = '0xh87dY78';
|
||||
|
||||
// when
|
||||
const res = getShortData(data);
|
||||
|
||||
// then
|
||||
expect(res).to.equal('0xh...');
|
||||
});
|
||||
|
||||
it('should return data as is', () => {
|
||||
// given
|
||||
const data = '0x0';
|
||||
|
||||
// when
|
||||
const shortData = getShortData(data);
|
||||
|
||||
// then
|
||||
expect(shortData).to.equal('0x0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFee', () => {
|
||||
it('should return wei BigNumber object equals to gas * gasPrice', () => {
|
||||
// given
|
||||
const gas = '0x76c0'; // 30400
|
||||
const gasPrice = '0x9184e72a000'; // 10000000000000 wei
|
||||
|
||||
// when
|
||||
const fee = getFee(gas, gasPrice);
|
||||
|
||||
// then
|
||||
expect(fee).to.be.an.instanceOf(BigNumber);
|
||||
expect(fee.toString()).to.be.equal('304000000000000000'); // converting to string due to https://github.com/MikeMcl/bignumber.js/issues/11
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTotalValue', () => {
|
||||
it('should return wei BigNumber totalValue equals to value + fee', () => {
|
||||
// given
|
||||
const fee = new BigNumber(304000000000000000); // wei
|
||||
const value = '0x9184e72a'; // 2441406250 wei
|
||||
|
||||
// when
|
||||
const totalValue = getTotalValue(fee, value);
|
||||
|
||||
// then
|
||||
expect(totalValue).to.be.an.instanceOf(BigNumber);
|
||||
expect(totalValue.toString()).to.be.equal('304000002441406250'); // converting to string due to https://github.com/MikeMcl/bignumber.js/issues/11
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,107 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
import { action, observable } from 'mobx';
|
||||
|
||||
export default class SignerStore {
|
||||
@observable balances = {};
|
||||
@observable localHashes = [];
|
||||
|
||||
externalLink = '';
|
||||
|
||||
constructor (api, withLocalTransactions = false, externalLink = '') {
|
||||
this._api = api;
|
||||
this._timeoutId = 0;
|
||||
this.externalLink = externalLink;
|
||||
|
||||
if (withLocalTransactions) {
|
||||
this.fetchLocalTransactions();
|
||||
}
|
||||
}
|
||||
|
||||
@action setBalance = (address, balance) => {
|
||||
this.setBalances({ [address]: balance });
|
||||
}
|
||||
|
||||
@action setBalances = (balances) => {
|
||||
this.balances = Object.assign({}, this.balances, balances);
|
||||
}
|
||||
|
||||
@action setLocalHashes = (localHashes = []) => {
|
||||
// Use slice to make sure they are both Arrays (MobX uses Objects for Observable Arrays)
|
||||
if (!isEqual(localHashes.slice(), this.localHashes.slice())) {
|
||||
this.localHashes = localHashes;
|
||||
}
|
||||
}
|
||||
|
||||
@action unsubscribe () {
|
||||
if (this._timeoutId) {
|
||||
clearTimeout(this._timeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
fetchBalance (address) {
|
||||
this._api.eth
|
||||
.getBalance(address)
|
||||
.then((balance) => {
|
||||
this.setBalance(address, balance);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('Store:fetchBalance', error);
|
||||
});
|
||||
}
|
||||
|
||||
fetchBalances (_addresses) {
|
||||
const addresses = _addresses.filter((address) => address) || [];
|
||||
|
||||
if (!addresses.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
Promise
|
||||
.all(addresses.map((address) => this._api.eth.getBalance(address)))
|
||||
.then((_balances) => {
|
||||
this.setBalances(
|
||||
addresses.reduce((balances, address, index) => {
|
||||
balances[address] = _balances[index];
|
||||
return balances;
|
||||
}, {})
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('Store:fetchBalances', error);
|
||||
});
|
||||
}
|
||||
|
||||
fetchLocalTransactions = () => {
|
||||
const nextTimeout = () => {
|
||||
this._timeoutId = setTimeout(this.fetchLocalTransactions, 1500);
|
||||
};
|
||||
|
||||
this._api.parity
|
||||
.localTransactions()
|
||||
.then((localTransactions) => {
|
||||
const keys = Object
|
||||
.keys(localTransactions)
|
||||
.filter((key) => localTransactions[key].status !== 'canceled');
|
||||
|
||||
this.setLocalHashes(keys);
|
||||
})
|
||||
.then(() => nextTimeout())
|
||||
.catch(() => nextTimeout());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user