diff --git a/backends/CLNRest.ts b/backends/CLNRest.ts index b68732de2..9b5167f73 100644 --- a/backends/CLNRest.ts +++ b/backends/CLNRest.ts @@ -73,54 +73,60 @@ export default class CLNRest { }) ); } else { - calls.set( - id, - ReactNativeBlobUtil.config({ - trusty: !certVerification - }) - .fetch( - method, - url, - headers, - data ? JSON.stringify(data) : data - ) - .then((response: any) => { - calls.delete(id); - if (response.info().status < 300) { - // handle ws responses - if (response.data.includes('\n')) { - const split = response.data.split('\n'); - const length = split.length; - // last instance is empty - return JSON.parse(split[length - 2]); - } - return response.json(); - } else { - try { - const errorInfo = response.json(); + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => reject(new Error('Request timeout')), 30000); + }); + + const fetchPromise = ReactNativeBlobUtil.config({ + trusty: !certVerification + }) + .fetch(method, url, headers, data ? JSON.stringify(data) : data) + .then((response: any) => { + calls.delete(id); + if (response.info().status < 300) { + // handle ws responses + if (response.data.includes('\n')) { + const split = response.data.split('\n'); + const length = split.length; + // last instance is empty + return JSON.parse(split[length - 2]); + } + return response.json(); + } else { + try { + const errorInfo = response.json(); + throw new Error( + (errorInfo.error && errorInfo.error.message) || + errorInfo.message || + errorInfo.error + ); + } catch (e) { + if ( + response.data && + typeof response.data === 'string' + ) { + throw new Error(response.data); + } else { throw new Error( - (errorInfo.error && - errorInfo.error.message) || - errorInfo.message || - errorInfo.error + localeString( + 'backends.LND.restReq.connectionError' + ) ); - } catch (e) { - if ( - response.data && - typeof response.data === 'string' - ) { - throw new Error(response.data); - } else { - throw new Error( - localeString( - 'backends.LND.restReq.connectionError' - ) - ); - } } } - }) - ); + } + }); + + const racePromise = Promise.race([ + fetchPromise, + timeoutPromise + ]).catch((error) => { + calls.delete(id); + console.log('Request timed out for:', url); + throw error; + }); + + calls.set(id, racePromise); } return await calls.get(id); diff --git a/backends/LND.ts b/backends/LND.ts index 762a0878a..8608cca59 100644 --- a/backends/LND.ts +++ b/backends/LND.ts @@ -52,54 +52,60 @@ export default class LND { }) ); } else { - calls.set( - id, - ReactNativeBlobUtil.config({ - trusty: !certVerification - }) - .fetch( - method, - url, - headers, - data ? JSON.stringify(data) : data - ) - .then((response: any) => { - calls.delete(id); - if (response.info().status < 300) { - // handle ws responses - if (response.data.includes('\n')) { - const split = response.data.split('\n'); - const length = split.length; - // last instance is empty - return JSON.parse(split[length - 2]); - } - return response.json(); - } else { - try { - const errorInfo = response.json(); + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => reject(new Error('Request timeout')), 30000); + }); + + const fetchPromise = ReactNativeBlobUtil.config({ + trusty: !certVerification + }) + .fetch(method, url, headers, data ? JSON.stringify(data) : data) + .then((response: any) => { + calls.delete(id); + if (response.info().status < 300) { + // handle ws responses + if (response.data.includes('\n')) { + const split = response.data.split('\n'); + const length = split.length; + // last instance is empty + return JSON.parse(split[length - 2]); + } + return response.json(); + } else { + try { + const errorInfo = response.json(); + throw new Error( + (errorInfo.error && errorInfo.error.message) || + errorInfo.message || + errorInfo.error + ); + } catch (e) { + if ( + response.data && + typeof response.data === 'string' + ) { + throw new Error(response.data); + } else { throw new Error( - (errorInfo.error && - errorInfo.error.message) || - errorInfo.message || - errorInfo.error + localeString( + 'backends.LND.restReq.connectionError' + ) ); - } catch (e) { - if ( - response.data && - typeof response.data === 'string' - ) { - throw new Error(response.data); - } else { - throw new Error( - localeString( - 'backends.LND.restReq.connectionError' - ) - ); - } } } - }) - ); + } + }); + + const racePromise = Promise.race([ + fetchPromise, + timeoutPromise + ]).catch((error) => { + calls.delete(id); + console.log('Request timed out for:', url); + throw error; + }); + + calls.set(id, racePromise); } return await calls.get(id); diff --git a/stores/NodeInfoStore.ts b/stores/NodeInfoStore.ts index c8b62a84b..e6b44f70b 100644 --- a/stores/NodeInfoStore.ts +++ b/stores/NodeInfoStore.ts @@ -59,7 +59,7 @@ export default class NodeInfoStore { this.errorMsg = ''; this.loading = true; const currentRequest = (this.currentRequest = {}); - return new Promise((resolve) => { + return new Promise((resolve, reject) => { BackendUtils.getMyNodeInfo() .then((data: any) => { if (this.currentRequest !== currentRequest) { @@ -81,7 +81,7 @@ export default class NodeInfoStore { // handle error this.errorMsg = errorToUserFriendly(error.toString()); this.getNodeInfoError(); - resolve(error); + reject(error); }); }); }; diff --git a/views/Wallet/Wallet.tsx b/views/Wallet/Wallet.tsx index bb145ba10..91659bceb 100644 --- a/views/Wallet/Wallet.tsx +++ b/views/Wallet/Wallet.tsx @@ -443,9 +443,13 @@ export default class Wallet extends React.Component { } } else if (implementation === 'lndhub') { if (connecting) { - await login({ login: username, password }).then(async () => { - BalanceStore.getLightningBalance(true); - }); + try { + await login({ login: username, password }); + await BalanceStore.getLightningBalance(true); + } catch (connectionError) { + console.log('LNDHub connection failed:', connectionError); + return; + } } else { BalanceStore.getLightningBalance(true); } @@ -455,22 +459,33 @@ export default class Wallet extends React.Component { error = await connect(); } if (!error) { - await BackendUtils.checkPerms(); - await NodeInfoStore.getNodeInfo(); - if (BackendUtils.supportsAccounts()) - await UTXOsStore.listAccounts(); - await BalanceStore.getCombinedBalance(); - if (BackendUtils.supportsChannelManagement()) - ChannelsStore.getChannels(); + try { + await BackendUtils.checkPerms(); + await NodeInfoStore.getNodeInfo(); + if (BackendUtils.supportsAccounts()) + await UTXOsStore.listAccounts(); + await BalanceStore.getCombinedBalance(); + if (BackendUtils.supportsChannelManagement()) + ChannelsStore.getChannels(); + } catch (connectionError) { + console.log('LNC connection failed:', connectionError); + return; + } } } else { - await NodeInfoStore.getNodeInfo(); - if (BackendUtils.supportsAccounts()) { - UTXOsStore.listAccounts(); + try { + await NodeInfoStore.getNodeInfo(); + if (BackendUtils.supportsAccounts()) { + UTXOsStore.listAccounts(); + } + await BalanceStore.getCombinedBalance(); + ChannelsStore.getChannels(); + } catch (connectionError) { + console.log('Node connection failed:', connectionError); + NodeInfoStore.getNodeInfoError(); + setConnectingStatus(false); + return; } - - await BalanceStore.getCombinedBalance(); - ChannelsStore.getChannels(); } if (