auto reconnect to the backend

pull/527/head
slowbackspace 5 years ago
parent f8e7320bf2
commit 8d399ff327

@ -1,6 +1,7 @@
/* @flow */ /* @flow */
import * as BLOCKCHAIN from 'actions/constants/blockchain'; import * as BLOCKCHAIN from 'actions/constants/blockchain';
import * as DiscoveryActions from 'actions/DiscoveryActions';
import * as EthereumBlockchainActions from 'actions/ethereum/BlockchainActions'; import * as EthereumBlockchainActions from 'actions/ethereum/BlockchainActions';
import * as RippleBlockchainActions from 'actions/ripple/BlockchainActions'; import * as RippleBlockchainActions from 'actions/ripple/BlockchainActions';
@ -19,6 +20,10 @@ export type BlockchainAction =
| { | {
type: typeof BLOCKCHAIN.START_SUBSCRIBE, type: typeof BLOCKCHAIN.START_SUBSCRIBE,
shortcut: string, shortcut: string,
}
| {
type: typeof BLOCKCHAIN.FAIL_SUBSCRIBE,
shortcut: string,
}; };
// Conditionally subscribe to blockchain backend // Conditionally subscribe to blockchain backend
@ -128,6 +133,8 @@ export const onError = (
const network = config.networks.find(c => c.shortcut === shortcut); const network = config.networks.find(c => c.shortcut === shortcut);
if (!network) return; if (!network) return;
dispatch(autoReconnect(shortcut));
switch (network.type) { switch (network.type) {
case 'ethereum': case 'ethereum':
await dispatch(EthereumBlockchainActions.onError(shortcut)); await dispatch(EthereumBlockchainActions.onError(shortcut));
@ -140,3 +147,28 @@ export const onError = (
break; break;
} }
}; };
const autoReconnect = (shortcut: string): PromiseAction<void> => async (
dispatch: Dispatch,
getState: GetState
): Promise<void> => {
const MAX_ATTEMPTS = 4;
let blockchain = getState().blockchain.find(b => b.shortcut === shortcut);
// try to automatically reconnect and wait after each attemp (5s * #attempt) untill max number of attemps is reached
for (let i = 0; i < MAX_ATTEMPTS; i++) {
const waitTime = 5000 * (i + 1); /// 5s * #attempt
if (!blockchain || blockchain.connected) {
break;
}
blockchain = getState().blockchain.find(b => b.shortcut === shortcut);
// reconnect with 7s timeout
// eslint-disable-next-line no-await-in-loop
await dispatch(DiscoveryActions.reconnect(shortcut, 7000));
// wait before next try
// eslint-disable-next-line no-await-in-loop
await new Promise(resolve => setTimeout(resolve, waitTime));
}
};

@ -1,6 +1,7 @@
/* @flow */ /* @flow */
import TrezorConnect, { UI } from 'trezor-connect'; import TrezorConnect, { UI } from 'trezor-connect';
import * as BLOCKCHAIN_ACTION from 'actions/constants/blockchain';
import * as DISCOVERY from 'actions/constants/discovery'; import * as DISCOVERY from 'actions/constants/discovery';
import * as ACCOUNT from 'actions/constants/account'; import * as ACCOUNT from 'actions/constants/account';
import * as NOTIFICATION from 'actions/constants/notification'; import * as NOTIFICATION from 'actions/constants/notification';
@ -325,11 +326,27 @@ const finish = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction
}); });
}; };
export const reconnect = (network: string): PromiseAction<void> => async ( export const reconnect = (network: string, timeout: number = 30): PromiseAction<void> => async (
dispatch: Dispatch dispatch: Dispatch
): Promise<void> => { ): Promise<void> => {
await dispatch(BlockchainActions.subscribe(network)); // Runs two promises.
dispatch(restore()); // First promise is a subscribe action which will never resolve in case of completely lost connection to the backend
// That's why there is a second promise that rejects after the specified timeout.
return Promise.race([
dispatch(BlockchainActions.subscribe(network)),
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)),
])
.catch(() => {
// catch error from first promises that rejects (most likely timeout)
dispatch({
type: BLOCKCHAIN_ACTION.FAIL_SUBSCRIBE,
shortcut: network,
});
})
.then(() => {
// dispatch restore when subscribe promise resolves
dispatch(restore());
});
}; };
// Called after DEVICE.CONNECT ('trezor-connect') or CONNECT.AUTH_DEVICE actions in WalletService // Called after DEVICE.CONNECT ('trezor-connect') or CONNECT.AUTH_DEVICE actions in WalletService

@ -1,5 +1,6 @@
/* @flow */ /* @flow */
export const START_SUBSCRIBE: 'blockchain__start_subscribe' = 'blockchain__start_subscribe'; export const START_SUBSCRIBE: 'blockchain__start_subscribe' = 'blockchain__start_subscribe';
export const FAIL_SUBSCRIBE: 'blockchain__fail_subscribe' = 'blockchain__fail_subscribe';
export const READY: 'blockchain__ready' = 'blockchain__ready'; export const READY: 'blockchain__ready' = 'blockchain__ready';
export const UPDATE_FEE: 'blockchain__update_fee' = 'blockchain__update_fee'; export const UPDATE_FEE: 'blockchain__update_fee' = 'blockchain__update_fee';

@ -48,6 +48,30 @@ const onStartSubscribe = (state: State, shortcut: string): State => {
]); ]);
}; };
const onFailSubscribe = (state: State, shortcut: string): State => {
const network = state.find(b => b.shortcut === shortcut);
if (network) {
const others = state.filter(b => b !== network);
return others.concat([
{
...network,
connecting: false,
},
]);
}
return state.concat([
{
shortcut,
connected: false,
connecting: false,
block: 0,
feeTimestamp: 0,
feeLevels: [],
},
]);
};
const onConnect = (state: State, action: BlockchainConnect): State => { const onConnect = (state: State, action: BlockchainConnect): State => {
const shortcut = action.payload.coin.shortcut.toLowerCase(); const shortcut = action.payload.coin.shortcut.toLowerCase();
const network = state.find(b => b.shortcut === shortcut); const network = state.find(b => b.shortcut === shortcut);
@ -136,6 +160,8 @@ export default (state: State = initialState, action: Action): State => {
switch (action.type) { switch (action.type) {
case BLOCKCHAIN_ACTION.START_SUBSCRIBE: case BLOCKCHAIN_ACTION.START_SUBSCRIBE:
return onStartSubscribe(state, action.shortcut); return onStartSubscribe(state, action.shortcut);
case BLOCKCHAIN_ACTION.FAIL_SUBSCRIBE:
return onFailSubscribe(state, action.shortcut);
case BLOCKCHAIN_EVENT.CONNECT: case BLOCKCHAIN_EVENT.CONNECT:
return onConnect(state, action); return onConnect(state, action);
case BLOCKCHAIN_EVENT.ERROR: case BLOCKCHAIN_EVENT.ERROR:

Loading…
Cancel
Save