mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-21 14:34:55 +01:00
ADD: Electrum connection toggle (#3470)
* ADD: Electrum connection toggle
This commit is contained in:
parent
c4b3fb3ae5
commit
5a61a8a41d
5 changed files with 189 additions and 114 deletions
|
@ -17,6 +17,7 @@ const ELECTRUM_HOST = 'electrum_host';
|
|||
const ELECTRUM_TCP_PORT = 'electrum_tcp_port';
|
||||
const ELECTRUM_SSL_PORT = 'electrum_ssl_port';
|
||||
const ELECTRUM_SERVER_HISTORY = 'electrum_server_history';
|
||||
const ELECTRUM_CONNECTION_DISABLED = 'electrum_disabled';
|
||||
|
||||
let _realm;
|
||||
async function _getRealm() {
|
||||
|
@ -74,7 +75,30 @@ let latestBlockheightTimestamp = false;
|
|||
|
||||
const txhashHeightCache = {};
|
||||
|
||||
async function isDisabled() {
|
||||
let isDisabled;
|
||||
try {
|
||||
const savedValue = await AsyncStorage.getItem(ELECTRUM_CONNECTION_DISABLED);
|
||||
if (savedValue === null) {
|
||||
isDisabled = false;
|
||||
} else {
|
||||
isDisabled = savedValue;
|
||||
}
|
||||
} catch {
|
||||
isDisabled = false;
|
||||
}
|
||||
return !!isDisabled;
|
||||
}
|
||||
|
||||
async function setDisabled(disabled = true) {
|
||||
return AsyncStorage.setItem(ELECTRUM_CONNECTION_DISABLED, disabled ? '1' : '');
|
||||
}
|
||||
|
||||
async function connectMain() {
|
||||
if (await isDisabled()) {
|
||||
console.log('Electrum connection disabled by user. Skipping connectMain call');
|
||||
return;
|
||||
}
|
||||
let usingPeer = await getRandomHardcodedPeer();
|
||||
const savedPeer = await getSavedPeer();
|
||||
if (savedPeer && savedPeer.host && (savedPeer.tcp || savedPeer.ssl)) {
|
||||
|
@ -162,6 +186,12 @@ async function connectMain() {
|
|||
connectMain();
|
||||
|
||||
async function presentNetworkErrorAlert(usingPeer) {
|
||||
if (await isDisabled()) {
|
||||
console.log(
|
||||
'Electrum connection disabled by user. Perhaps we are attempting to show this network error alert after the user disabled connections.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
Alert.alert(
|
||||
loc.errors.network,
|
||||
loc.formatString(
|
||||
|
@ -636,6 +666,10 @@ module.exports.multiGetTransactionByTxid = async function (txids, batchsize, ver
|
|||
module.exports.waitTillConnected = async function () {
|
||||
let waitTillConnectedInterval = false;
|
||||
let retriesCounter = 0;
|
||||
if (await isDisabled()) {
|
||||
console.warn('Electrum connections disabled by user. waitTillConnected skipping...');
|
||||
return;
|
||||
}
|
||||
return new Promise(function (resolve, reject) {
|
||||
waitTillConnectedInterval = setInterval(() => {
|
||||
if (mainConnected) {
|
||||
|
@ -851,7 +885,9 @@ module.exports.setBatchingDisabled = () => {
|
|||
module.exports.setBatchingEnabled = () => {
|
||||
disableBatching = false;
|
||||
};
|
||||
|
||||
module.exports.connectMain = connectMain;
|
||||
module.exports.isDisabled = isDisabled;
|
||||
module.exports.setDisabled = setDisabled;
|
||||
module.exports.hardcodedPeers = hardcodedPeers;
|
||||
module.exports.getRandomHardcodedPeer = getRandomHardcodedPeer;
|
||||
module.exports.ELECTRUM_HOST = ELECTRUM_HOST;
|
||||
|
|
|
@ -263,6 +263,8 @@
|
|||
"electrum_connected_not": "Not Connected",
|
||||
"electrum_error_connect": "Can’t connect to the provided Electrum server",
|
||||
"electrum_host": "E.g. {example}",
|
||||
"electrum_offline_mode": "Offline Mode",
|
||||
"electrum_offline_description": "When enabled, your Bitcoin wallets will not attempt to fetch balances or transactions.",
|
||||
"electrum_port": "Port, usually {example}",
|
||||
"use_ssl": "Use SSL",
|
||||
"electrum_saved": "Your changes have been saved successfully. Restarting BlueWallet may be required for the changes to take effect.",
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Switch,
|
||||
Pressable,
|
||||
} from 'react-native';
|
||||
import DefaultPreference from 'react-native-default-preference';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
@ -30,6 +31,7 @@ import {
|
|||
SafeBlueArea,
|
||||
BlueDoneAndDismissKeyboardInputAccessory,
|
||||
BlueDismissKeyboardInputAccessory,
|
||||
BlueListItem,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import { isTorCapable } from '../../blue_modules/environment';
|
||||
|
@ -44,6 +46,7 @@ export default class ElectrumSettings extends Component {
|
|||
const server = props?.route?.params?.server;
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
isOfflineMode: false,
|
||||
serverHistory: [],
|
||||
config: {},
|
||||
server,
|
||||
|
@ -61,6 +64,7 @@ export default class ElectrumSettings extends Component {
|
|||
const port = await AsyncStorage.getItem(BlueElectrum.ELECTRUM_TCP_PORT);
|
||||
const sslPort = await AsyncStorage.getItem(BlueElectrum.ELECTRUM_SSL_PORT);
|
||||
const serverHistoryStr = await AsyncStorage.getItem(BlueElectrum.ELECTRUM_SERVER_HISTORY);
|
||||
const isOfflineMode = await BlueElectrum.isDisabled();
|
||||
const serverHistory = JSON.parse(serverHistoryStr) || [];
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
|
@ -68,6 +72,7 @@ export default class ElectrumSettings extends Component {
|
|||
port,
|
||||
sslPort,
|
||||
serverHistory,
|
||||
isOfflineMode,
|
||||
isAndroidNumericKeyboardFocused: false,
|
||||
isAndroidAddressKeyboardVisible: false,
|
||||
});
|
||||
|
@ -248,7 +253,18 @@ export default class ElectrumSettings extends Component {
|
|||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
onElectrumConnectionEnabledSwitchValueChangd = async value => {
|
||||
if (value === true) {
|
||||
await BlueElectrum.setDisabled(true);
|
||||
BlueElectrum.forceDisconnect();
|
||||
} else {
|
||||
await BlueElectrum.setDisabled(false);
|
||||
BlueElectrum.connectMain();
|
||||
}
|
||||
this.setState({ isOfflineMode: value });
|
||||
};
|
||||
|
||||
renderElectrumSettings = () => {
|
||||
const serverHistoryItems = this.state.serverHistory.map((server, i) => {
|
||||
return (
|
||||
<View key={i} style={styles.serverHistoryItem}>
|
||||
|
@ -262,133 +278,151 @@ export default class ElectrumSettings extends Component {
|
|||
});
|
||||
|
||||
return (
|
||||
<SafeBlueArea>
|
||||
<ScrollView keyboardShouldPersistTaps="always">
|
||||
<>
|
||||
<BlueCard>
|
||||
<BlueText style={styles.status}>{loc.settings.electrum_status}</BlueText>
|
||||
<View style={styles.connectWrap}>
|
||||
<View style={[styles.container, this.state.config.connected === 1 ? styles.containerConnected : styles.containerDisconnected]}>
|
||||
<BlueText style={this.state.config.connected === 1 ? styles.textConnected : styles.textDisconnected}>
|
||||
{this.state.config.connected === 1 ? loc.settings.electrum_connected : loc.settings.electrum_connected_not}
|
||||
</BlueText>
|
||||
</View>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<BlueText style={styles.hostname} onPress={this.checkServer}>
|
||||
{this.state.config.host}:{this.state.config.port}
|
||||
</BlueText>
|
||||
</BlueCard>
|
||||
<KeyboardAvoidingView>
|
||||
<BlueCard>
|
||||
<BlueText style={styles.status}>{loc.settings.electrum_status}</BlueText>
|
||||
<View style={styles.connectWrap}>
|
||||
<View
|
||||
style={[styles.container, this.state.config.connected === 1 ? styles.containerConnected : styles.containerDisconnected]}
|
||||
>
|
||||
<BlueText style={this.state.config.connected === 1 ? styles.textConnected : styles.textDisconnected}>
|
||||
{this.state.config.connected === 1 ? loc.settings.electrum_connected : loc.settings.electrum_connected_not}
|
||||
</BlueText>
|
||||
</View>
|
||||
<View style={styles.inputWrap}>
|
||||
<TextInput
|
||||
placeholder={
|
||||
loc.formatString(loc.settings.electrum_host, { example: '111.222.333.111' }) +
|
||||
(isTorCapable ? ' (' + loc.settings.tor_supported + ')' : '')
|
||||
}
|
||||
value={this.state.host}
|
||||
onChangeText={text => this.setState({ host: text.trim() })}
|
||||
numberOfLines={1}
|
||||
style={styles.inputText}
|
||||
editable={!this.state.isLoading}
|
||||
placeholderTextColor="#81868e"
|
||||
autoCorrect={false}
|
||||
autoCapitalize="none"
|
||||
underlineColorAndroid="transparent"
|
||||
inputAccessoryViewID={BlueDoneAndDismissKeyboardInputAccessory.InputAccessoryViewID}
|
||||
testID="HostInput"
|
||||
onFocus={() => this.setState({ isAndroidAddressKeyboardVisible: true })}
|
||||
onBlur={() => this.setState({ isAndroidAddressKeyboardVisible: false })}
|
||||
/>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<BlueText style={styles.hostname} onPress={this.checkServer}>
|
||||
{this.state.config.host}:{this.state.config.port}
|
||||
</BlueText>
|
||||
</BlueCard>
|
||||
<KeyboardAvoidingView>
|
||||
<BlueCard>
|
||||
<View style={styles.portWrap}>
|
||||
<View style={styles.inputWrap}>
|
||||
<TextInput
|
||||
placeholder={
|
||||
loc.formatString(loc.settings.electrum_host, { example: '111.222.333.111' }) +
|
||||
(isTorCapable ? ' (' + loc.settings.tor_supported + ')' : '')
|
||||
placeholder={loc.formatString(loc.settings.electrum_port, { example: '50001' })}
|
||||
value={this.state.sslPort?.trim() === '' || this.state.sslPort === null ? this.state.port : this.state.sslPort}
|
||||
onChangeText={text =>
|
||||
this.setState(prevState => {
|
||||
if (prevState.sslPort?.trim() === '') {
|
||||
return { port: text.trim(), sslPort: '' };
|
||||
} else {
|
||||
return { port: '', sslPort: text.trim() };
|
||||
}
|
||||
})
|
||||
}
|
||||
value={this.state.host}
|
||||
onChangeText={text => this.setState({ host: text.trim() })}
|
||||
numberOfLines={1}
|
||||
style={styles.inputText}
|
||||
editable={!this.state.isLoading}
|
||||
placeholderTextColor="#81868e"
|
||||
underlineColorAndroid="transparent"
|
||||
autoCorrect={false}
|
||||
autoCapitalize="none"
|
||||
underlineColorAndroid="transparent"
|
||||
inputAccessoryViewID={BlueDoneAndDismissKeyboardInputAccessory.InputAccessoryViewID}
|
||||
testID="HostInput"
|
||||
onFocus={() => this.setState({ isAndroidAddressKeyboardVisible: true })}
|
||||
onBlur={() => this.setState({ isAndroidAddressKeyboardVisible: false })}
|
||||
keyboardType="number-pad"
|
||||
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
|
||||
testID="PortInput"
|
||||
onFocus={() => this.setState({ isAndroidNumericKeyboardFocused: true })}
|
||||
onBlur={() => this.setState({ isAndroidNumericKeyboardFocused: false })}
|
||||
/>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.portWrap}>
|
||||
<View style={styles.inputWrap}>
|
||||
<TextInput
|
||||
placeholder={loc.formatString(loc.settings.electrum_port, { example: '50001' })}
|
||||
value={this.state.sslPort?.trim() === '' || this.state.sslPort === null ? this.state.port : this.state.sslPort}
|
||||
onChangeText={text =>
|
||||
this.setState(prevState => {
|
||||
if (prevState.sslPort?.trim() === '') {
|
||||
return { port: text.trim(), sslPort: '' };
|
||||
} else {
|
||||
return { port: '', sslPort: text.trim() };
|
||||
}
|
||||
})
|
||||
}
|
||||
numberOfLines={1}
|
||||
style={styles.inputText}
|
||||
editable={!this.state.isLoading}
|
||||
placeholderTextColor="#81868e"
|
||||
underlineColorAndroid="transparent"
|
||||
autoCorrect={false}
|
||||
autoCapitalize="none"
|
||||
keyboardType="number-pad"
|
||||
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
|
||||
testID="PortInput"
|
||||
onFocus={() => this.setState({ isAndroidNumericKeyboardFocused: true })}
|
||||
onBlur={() => this.setState({ isAndroidNumericKeyboardFocused: false })}
|
||||
/>
|
||||
</View>
|
||||
<BlueText style={styles.usePort}>{loc.settings.use_ssl}</BlueText>
|
||||
<Switch testID="SSLPortInput" value={this.state.sslPort?.trim() > 0} onValueChange={this.useSSLPortToggled} />
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<BlueText style={styles.usePort}>{loc.settings.use_ssl}</BlueText>
|
||||
<Switch testID="SSLPortInput" value={this.state.sslPort?.trim() > 0} onValueChange={this.useSSLPortToggled} />
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
|
||||
<View style={styles.serverAddTitle}>
|
||||
<BlueText style={styles.explain}>{loc.settings.electrum_settings_explain}</BlueText>
|
||||
<TouchableOpacity accessibilityRole="button" testID="ResetToDefault" onPress={() => this.resetToDefault()}>
|
||||
<BlueText>{loc.settings.electrum_reset}</BlueText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
{this.state.isLoading ? <BlueLoading /> : <BlueButton testID="Save" onPress={this.save} title={loc.settings.save} />}
|
||||
<BlueSpacing20 />
|
||||
<BlueButtonLink title={loc.wallets.import_scan_qr} onPress={this.importScan} />
|
||||
<BlueSpacing20 />
|
||||
</BlueCard>
|
||||
{Platform.select({
|
||||
ios: <BlueDismissKeyboardInputAccessory />,
|
||||
android: this.state.isAndroidNumericKeyboardFocused && <BlueDismissKeyboardInputAccessory />,
|
||||
})}
|
||||
<View style={styles.serverAddTitle}>
|
||||
<BlueText style={styles.explain}>{loc.settings.electrum_settings_explain}</BlueText>
|
||||
<TouchableOpacity accessibilityRole="button" testID="ResetToDefault" onPress={() => this.resetToDefault()}>
|
||||
<BlueText>{loc.settings.electrum_reset}</BlueText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
{this.state.isLoading ? <BlueLoading /> : <BlueButton testID="Save" onPress={this.save} title={loc.settings.save} />}
|
||||
<BlueSpacing20 />
|
||||
<BlueButtonLink title={loc.wallets.import_scan_qr} onPress={this.importScan} />
|
||||
<BlueSpacing20 />
|
||||
</BlueCard>
|
||||
{Platform.select({
|
||||
ios: <BlueDismissKeyboardInputAccessory />,
|
||||
android: this.state.isAndroidNumericKeyboardFocused && <BlueDismissKeyboardInputAccessory />,
|
||||
})}
|
||||
|
||||
{Platform.select({
|
||||
ios: (
|
||||
<BlueDoneAndDismissKeyboardInputAccessory
|
||||
onClearTapped={() => this.setState({ host: '' })}
|
||||
onPasteTapped={text => {
|
||||
this.setState({ host: text });
|
||||
Keyboard.dismiss();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
android: this.state.isAndroidAddressKeyboardVisible && (
|
||||
<BlueDoneAndDismissKeyboardInputAccessory
|
||||
onClearTapped={() => {
|
||||
this.setState({ host: '' });
|
||||
Keyboard.dismiss();
|
||||
}}
|
||||
onPasteTapped={text => {
|
||||
this.setState({ host: text });
|
||||
Keyboard.dismiss();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
})}
|
||||
</KeyboardAvoidingView>
|
||||
{serverHistoryItems.length > 0 && !this.state.isLoading && (
|
||||
<BlueCard>
|
||||
<View style={styles.serverHistoryTitle}>
|
||||
<BlueText style={styles.explain}>{loc.settings.electrum_history}</BlueText>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={() => this.clearHistoryAlert()}>
|
||||
<BlueText>{loc.settings.electrum_clear}</BlueText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{serverHistoryItems}
|
||||
</BlueCard>
|
||||
)}
|
||||
{Platform.select({
|
||||
ios: (
|
||||
<BlueDoneAndDismissKeyboardInputAccessory
|
||||
onClearTapped={() => this.setState({ host: '' })}
|
||||
onPasteTapped={text => {
|
||||
this.setState({ host: text });
|
||||
Keyboard.dismiss();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
android: this.state.isAndroidAddressKeyboardVisible && (
|
||||
<BlueDoneAndDismissKeyboardInputAccessory
|
||||
onClearTapped={() => {
|
||||
this.setState({ host: '' });
|
||||
Keyboard.dismiss();
|
||||
}}
|
||||
onPasteTapped={text => {
|
||||
this.setState({ host: text });
|
||||
Keyboard.dismiss();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
})}
|
||||
</KeyboardAvoidingView>
|
||||
{serverHistoryItems.length > 0 && !this.state.isLoading && (
|
||||
<BlueCard>
|
||||
<View style={styles.serverHistoryTitle}>
|
||||
<BlueText style={styles.explain}>{loc.settings.electrum_history}</BlueText>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={() => this.clearHistoryAlert()}>
|
||||
<BlueText>{loc.settings.electrum_clear}</BlueText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{serverHistoryItems}
|
||||
</BlueCard>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SafeBlueArea>
|
||||
<ScrollView keyboardShouldPersistTaps="always">
|
||||
<BlueListItem
|
||||
Component={Pressable}
|
||||
title={loc.settings.electrum_offline_mode}
|
||||
switch={{
|
||||
onValueChange: this.onElectrumConnectionEnabledSwitchValueChangd,
|
||||
value: this.state.isOfflineMode,
|
||||
testID: 'ElectrumConnectionEnabledSwitch',
|
||||
}}
|
||||
/>
|
||||
<BlueCard>
|
||||
<BlueText>{loc.settings.electrum_offline_description}</BlueText>
|
||||
</BlueCard>
|
||||
{!this.state.isOfflineMode && this.renderElectrumSettings()}
|
||||
</ScrollView>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
|
|
|
@ -29,6 +29,7 @@ import { isDesktop, isMacCatalina, isTablet } from '../../blue_modules/environme
|
|||
import BlueClipboard from '../../blue_modules/clipboard';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
|
||||
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
||||
const scanqrHelper = require('../../helpers/scan-qr');
|
||||
const A = require('../../blue_modules/analytics');
|
||||
const fs = require('../../blue_modules/fs');
|
||||
|
@ -141,7 +142,8 @@ const WalletsList = () => {
|
|||
* Forcefully fetches TXs and balance for ALL wallets.
|
||||
* Triggered manually by user on pull-to-refresh.
|
||||
*/
|
||||
const refreshTransactions = (showLoadingIndicator = true, showUpdateStatusIndicator = false) => {
|
||||
const refreshTransactions = async (showLoadingIndicator = true, showUpdateStatusIndicator = false) => {
|
||||
if (await BlueElectrum.isDisabled()) return setIsLoading(false);
|
||||
setIsLoading(showLoadingIndicator);
|
||||
refreshAllWalletTransactions(showLoadingIndicator, showUpdateStatusIndicator).finally(() => setIsLoading(false));
|
||||
};
|
||||
|
|
|
@ -175,6 +175,7 @@ const WalletTransactions = () => {
|
|||
* Forcefully fetches TXs and balance for wallet
|
||||
*/
|
||||
const refreshTransactions = async () => {
|
||||
if (await BlueElectrum.isDisabled()) return setIsLoading(false);
|
||||
if (isLoading) return;
|
||||
setIsLoading(true);
|
||||
let noErr = true;
|
||||
|
|
Loading…
Add table
Reference in a new issue