MyCrypto/common/sagas/config/node.ts

224 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { delay, SagaIterator } from 'redux-saga';
import {
call,
cancel,
fork,
put,
take,
takeEvery,
select,
apply,
takeLatest
} from 'redux-saga/effects';
import {
getNodeId,
getNodeConfig,
getOffline,
isStaticNodeId,
getCustomNodeFromId,
getStaticNodeFromId,
getNetworkConfigById
} from 'selectors/config';
import { TypeKeys } from 'actions/config/constants';
import {
toggleOffline,
changeNode,
changeNodeIntent,
setLatestBlock,
AddCustomNodeAction,
ChangeNodeForceAction,
ChangeNodeIntentAction
} from 'actions/config';
import { showNotification } from 'actions/notifications';
import { resetWallet } from 'actions/wallet';
import { translateRaw } from 'translations';
import { StaticNodeConfig, CustomNodeConfig, NodeConfig } from 'types/node';
import { CustomNetworkConfig, StaticNetworkConfig } from 'types/network';
import {
getShepherdOffline,
isAutoNode,
shepherd,
shepherdProvider,
stripWeb3Network,
makeProviderConfig,
getShepherdNetwork
} from 'libs/nodes';
export function* pollOfflineStatus(): SagaIterator {
let hasCheckedOnline = false;
while (true) {
const isOffline: boolean = yield select(getOffline);
// If our offline state disagrees with the browser, run a check
// Don't check if the user is in another tab or window
const shouldPing = !hasCheckedOnline || navigator.onLine === isOffline;
if (shouldPing && !document.hidden) {
const pingSucceeded = yield call(getShepherdOffline);
if (pingSucceeded && isOffline) {
// If we were able to ping but redux says we're offline, mark online
yield put(
showNotification('success', 'Your connection to the network has been restored!', 3000)
);
yield put(toggleOffline());
} else if (!pingSucceeded && !isOffline) {
// If we were unable to ping but redux says we're online, mark offline
// If they had been online, show an error.
// If they hadn't been online, just inform them with a warning.
if (hasCheckedOnline) {
yield put(
showNotification(
'danger',
`Youve lost your connection to the network, check your internet
connection or try changing networks from the dropdown at the
top right of the page.`,
Infinity
)
);
} else {
yield put(
showNotification(
'info',
'You are currently offline. Some features will be unavailable.',
5000
)
);
}
yield put(toggleOffline());
} else {
// If neither case was true, try again in 5s
yield call(delay, 5000);
}
hasCheckedOnline = true;
} else {
yield call(delay, 1000);
}
}
}
// Fork our recurring API call, watch for the need to cancel.
export function* handlePollOfflineStatus(): SagaIterator {
const pollOfflineStatusTask = yield fork(pollOfflineStatus);
yield take('CONFIG_STOP_POLL_OFFLINE_STATE');
yield cancel(pollOfflineStatusTask);
}
// @HACK For now we reload the app when doing a language swap to force non-connected
// data to reload. Also the use of timeout to avoid using additional actions for now.
export function* reload(): SagaIterator {
setTimeout(() => location.reload(), 1150);
}
export function* handleNodeChangeIntent({
payload: nodeIdToSwitchTo
}: ChangeNodeIntentAction): SagaIterator {
const isStaticNode: boolean = yield select(isStaticNodeId, nodeIdToSwitchTo);
const currentConfig: NodeConfig = yield select(getNodeConfig);
function* bailOut(message: string) {
const currentNodeId: string = yield select(getNodeId);
yield put(showNotification('danger', message, 5000));
yield put(changeNode({ networkId: currentConfig.network, nodeId: currentNodeId }));
}
let nextNodeConfig: CustomNodeConfig | StaticNodeConfig;
if (!isStaticNode) {
const config: CustomNodeConfig | undefined = yield select(
getCustomNodeFromId,
nodeIdToSwitchTo
);
if (config) {
nextNodeConfig = config;
} else {
return yield* bailOut(`Attempted to switch to unknown node '${nodeIdToSwitchTo}'`);
}
} else {
nextNodeConfig = yield select(getStaticNodeFromId, nodeIdToSwitchTo);
}
const nextNetwork: StaticNetworkConfig | CustomNetworkConfig = yield select(
getNetworkConfigById,
stripWeb3Network(nextNodeConfig.network)
);
if (!nextNetwork) {
return yield* bailOut(
`Unknown custom network for your node '${nodeIdToSwitchTo}', try re-adding it`
);
}
if (isAutoNode(nodeIdToSwitchTo)) {
shepherd.auto();
if (getShepherdNetwork() !== nextNodeConfig.network) {
yield apply(shepherd, shepherd.switchNetworks, [nextNodeConfig.network]);
}
} else {
try {
yield apply(shepherd, shepherd.manual, [nodeIdToSwitchTo, false]);
} catch (err) {
console.error(err);
return yield* bailOut(translateRaw('ERROR_32'));
}
}
let currentBlock;
try {
currentBlock = yield apply(shepherdProvider, shepherdProvider.getCurrentBlock);
} catch (err) {
console.error(err);
return yield* bailOut(translateRaw('ERROR_32'));
}
yield put(setLatestBlock(currentBlock));
yield put(changeNode({ networkId: nextNodeConfig.network, nodeId: nodeIdToSwitchTo }));
if (currentConfig.network !== nextNodeConfig.network) {
yield fork(handleNewNetwork);
}
}
export function* handleAddCustomNode(action: AddCustomNodeAction): SagaIterator {
const { payload: { config } } = action;
shepherd.useProvider(
'myccustom',
config.id,
makeProviderConfig({ network: config.network }),
config
);
yield put(changeNodeIntent(action.payload.id));
}
export function* handleNewNetwork() {
yield put(resetWallet());
}
export function* handleNodeChangeForce({ payload: staticNodeIdToSwitchTo }: ChangeNodeForceAction) {
// does not perform node online check before changing nodes
// necessary when switching back from Web3 provider so node
// dropdown does not get stuck if node is offline
const isStaticNode: boolean = yield select(isStaticNodeId, staticNodeIdToSwitchTo);
if (!isStaticNode) {
return;
}
const nodeConfig = yield select(getStaticNodeFromId, staticNodeIdToSwitchTo);
// force the node change
yield put(changeNode({ networkId: nodeConfig.network, nodeId: staticNodeIdToSwitchTo }));
// also put the change through as usual so status check and
// error messages occur if the node is unavailable
yield put(changeNodeIntent(staticNodeIdToSwitchTo));
}
export const node = [
takeEvery(TypeKeys.CONFIG_NODE_CHANGE_INTENT, handleNodeChangeIntent),
takeEvery(TypeKeys.CONFIG_NODE_CHANGE_FORCE, handleNodeChangeForce),
takeLatest(TypeKeys.CONFIG_POLL_OFFLINE_STATUS, handlePollOfflineStatus),
takeEvery(TypeKeys.CONFIG_LANGUAGE_CHANGE, reload),
takeEvery(TypeKeys.CONFIG_ADD_CUSTOM_NODE, handleAddCustomNode)
];