Refresh UI on nodeKind changes, e.g. personal -> public (#5312)

* Poll details based on nodeKind

* Delay long polling when public/light

* Reload UI when nodeKind changed

* Fix tests (not using dispatch, reload instead)

* PR grumbles/cleanups
This commit is contained in:
Jaco Greeff 2017-04-20 17:31:15 +02:00 committed by GitHub
parent ee25249729
commit ba03ed4eea
4 changed files with 86 additions and 34 deletions

View File

@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import BalancesProvider from './balances';
import { showSnackbar } from './snackbarActions';
import { DEFAULT_NETCHAIN } from './statusReducer'; import { DEFAULT_NETCHAIN } from './statusReducer';
export default class ChainMiddleware { export default class ChainMiddleware {
@ -24,20 +22,34 @@ export default class ChainMiddleware {
if (action.type === 'statusCollection') { if (action.type === 'statusCollection') {
const { collection } = action; const { collection } = action;
if (collection && collection.netChain) { if (collection) {
const newChain = collection.netChain;
const { nodeStatus } = store.getState(); const { nodeStatus } = store.getState();
const { netChain, nodeKind } = nodeStatus;
const newChain = collection.netChain;
const newNodeKind = collection.nodeKind;
let reloadChain = false;
let reloadType = false;
if (newChain !== nodeStatus.netChain && nodeStatus.netChain !== DEFAULT_NETCHAIN) { // force reload when chain has changed and is not initial value
store.dispatch(showSnackbar(`Switched to ${newChain}. The UI will reload now...`)); if (newChain) {
const hasChainChanged = newChain !== netChain;
const isInitialChain = netChain === DEFAULT_NETCHAIN;
reloadChain = !isInitialChain && hasChainChanged;
}
// force reload when nodeKind (availability or capability) has changed
if (newNodeKind && nodeKind) {
const hasAvailabilityChanged = nodeKind.availability !== newNodeKind.availability;
const hasCapabilityChanged = nodeKind.capability !== newNodeKind.capability;
reloadType = hasAvailabilityChanged || hasCapabilityChanged;
}
if (reloadChain || reloadType) {
setTimeout(() => { setTimeout(() => {
window.location.reload(); window.location.reload();
}, 0); }, 0);
// Fetch the new balances without notifying the user of any change
BalancesProvider.get(store).fetchAllBalances({
changedNetwork: true
});
} }
} }
} }

View File

@ -24,11 +24,22 @@ import { createWsApi } from '~/../test/e2e/ethapi';
let middleware; let middleware;
let next; let next;
let store; let store;
let clock;
const api = createWsApi(); const api = createWsApi();
Contracts.create(api); Contracts.create(api);
function stubGlobals () {
clock = sinon.useFakeTimers();
sinon.spy(window.location, 'reload');
}
function restoreGlobals () {
window.location.reload.restore();
clock.restore();
}
function createMiddleware (collection = {}) { function createMiddleware (collection = {}) {
middleware = new ChainMiddleware().toMiddleware(); middleware = new ChainMiddleware().toMiddleware();
next = sinon.stub(); next = sinon.stub();
@ -46,10 +57,22 @@ function createMiddleware (collection = {}) {
} }
function callMiddleware (action) { function callMiddleware (action) {
return middleware(store)(next)(action); const result = middleware(store)(next)(action);
clock.tick(100);
return result;
} }
describe('reduxs/providers/ChainMiddleware', () => { describe('reduxs/providers/ChainMiddleware', () => {
beforeEach(() => {
stubGlobals();
});
afterEach(() => {
restoreGlobals();
});
describe('next action', () => { describe('next action', () => {
beforeEach(() => { beforeEach(() => {
createMiddleware(); createMiddleware();
@ -69,25 +92,25 @@ describe('reduxs/providers/ChainMiddleware', () => {
}); });
describe('chain switching', () => { describe('chain switching', () => {
it('does not dispatch when moving from the initial/unknown chain', () => { it('does not reload when moving from the initial/unknown chain', () => {
createMiddleware(); createMiddleware();
callMiddleware({ type: 'statusCollection', collection: { netChain: 'homestead' } }); callMiddleware({ type: 'statusCollection', collection: { netChain: 'homestead' } });
expect(store.dispatch).not.to.have.been.called; expect(window.location.reload).not.to.have.been.called;
}); });
it('does not dispatch when moving to the same chain', () => { it('does not reload when moving to the same chain', () => {
createMiddleware({ netChain: 'homestead' }); createMiddleware({ netChain: 'homestead' });
callMiddleware({ type: 'statusCollection', collection: { netChain: 'homestead' } }); callMiddleware({ type: 'statusCollection', collection: { netChain: 'homestead' } });
expect(store.dispatch).not.to.have.been.called; expect(window.location.reload).not.to.have.been.called;
}); });
it('does dispatch when moving between chains', () => { it('does reload when moving between chains', () => {
createMiddleware({ netChain: 'homestead' }); createMiddleware({ netChain: 'homestead' });
callMiddleware({ type: 'statusCollection', collection: { netChain: 'ropsten' } }); callMiddleware({ type: 'statusCollection', collection: { netChain: 'ropsten' } });
expect(store.dispatch).to.have.been.called; expect(window.location.reload).to.have.been.called;
}); });
}); });
}); });

View File

@ -156,7 +156,8 @@ export default class Status {
} }
_pollTraceMode = () => { _pollTraceMode = () => {
return this._api.trace.block() return this._api.trace
.block()
.then(blockTraces => { .then(blockTraces => {
// Assumes not in Trace Mode if no transactions // Assumes not in Trace Mode if no transactions
// in latest block... // in latest block...
@ -168,14 +169,12 @@ export default class Status {
getApiStatus = () => { getApiStatus = () => {
const { isConnected, isConnecting, needsToken, secureToken } = this._api; const { isConnected, isConnecting, needsToken, secureToken } = this._api;
const apiStatus = { return {
isConnected, isConnected,
isConnecting, isConnecting,
needsToken, needsToken,
secureToken secureToken
}; };
return apiStatus;
} }
_pollStatus = () => { _pollStatus = () => {
@ -194,7 +193,10 @@ export default class Status {
return Promise.resolve(); return Promise.resolve();
} }
const statusPromises = [ this._api.eth.syncing(), this._api.parity.netPeers() ]; const statusPromises = [
this._api.eth.syncing(),
this._api.parity.netPeers()
];
return Promise return Promise
.all(statusPromises) .all(statusPromises)
@ -225,7 +227,10 @@ export default class Status {
return Promise.resolve(); return Promise.resolve();
} }
const nextTimeout = (timeout = 30000) => { const { nodeKindFull } = this._store.getState().nodeStatus;
const defaultTimeout = (nodeKindFull === false ? 240 : 30) * 1000;
const nextTimeout = (timeout = defaultTimeout) => {
if (this._timeoutIds.longStatus) { if (this._timeoutIds.longStatus) {
clearTimeout(this._timeoutIds.longStatus); clearTimeout(this._timeoutIds.longStatus);
} }
@ -233,24 +238,34 @@ export default class Status {
this._timeoutIds.longStatus = setTimeout(() => this._pollLongStatus(), timeout); this._timeoutIds.longStatus = setTimeout(() => this._pollLongStatus(), timeout);
}; };
const statusPromises = [
this._api.parity.nodeKind(),
this._api.parity.netPeers(),
this._api.web3.clientVersion(),
this._api.net.version(),
this._api.parity.netChain()
];
if (nodeKindFull) {
statusPromises.push(this._upgradeStore.checkUpgrade());
}
return Promise return Promise
.all([ .all(statusPromises)
this._api.parity.netPeers(), .then(([nodeKind, netPeers, clientVersion, netVersion, netChain]) => {
this._api.web3.clientVersion(),
this._api.net.version(),
this._api.parity.netChain(),
this._upgradeStore.checkUpgrade()
])
.then(([
netPeers, clientVersion, netVersion, netChain, upgradeStatus
]) => {
const isTest = [ const isTest = [
'2', // morden '2', // morden
'3', // ropsten '3', // ropsten
'42' // kovan '42' // kovan
].includes(netVersion); ].includes(netVersion);
const nodeKindFull = nodeKind &&
nodeKind.availability === 'personal' &&
nodeKind.capability === 'full';
const longStatus = { const longStatus = {
nodeKind,
nodeKindFull,
netPeers, netPeers,
clientVersion, clientVersion,
netChain, netChain,

View File

@ -31,6 +31,8 @@ const initialState = {
peers: [] peers: []
}, },
netVersion: '0', netVersion: '0',
nodeKind: null,
nodeKindFull: null,
syncing: true, syncing: true,
isConnected: false, isConnected: false,
isConnecting: false, isConnecting: false,