Browse Source

FIX: Create button was difficult to press on android. (#369)

FIX: Create button was difficult to press on android.
ADD: Clipboard detection on app state change to foreground
OPS: Upgrade to RN 58.6 due to various important fixes
FIX: Statusbar restored
icloud v3.8.2
Marcos Rodriguez Vélez 6 years ago
committed by Igor Korsakov
parent
commit
6017f4d990
  1. 125
      App.js
  2. 6
      BlueComponents.js
  3. 2
      Privacy.js
  4. 64
      package-lock.json
  5. 2
      package.json
  6. 10
      screen/send/details.js

125
App.js

@ -1,22 +1,76 @@
import React from 'react'; import React from 'react';
import { Linking } from 'react-native'; import { Linking, AppState, Clipboard, StyleSheet, KeyboardAvoidingView, Platform, View } from 'react-native';
import Modal from 'react-native-modal';
import { NavigationActions } from 'react-navigation'; import { NavigationActions } from 'react-navigation';
import MainBottomTabs from './MainBottomTabs'; import MainBottomTabs from './MainBottomTabs';
import NavigationService from './NavigationService'; import NavigationService from './NavigationService';
import { BlueTextCentered, BlueButton } from './BlueComponents';
const bitcoin = require('bitcoinjs-lib');
const bitcoinModalString = 'Bitcoin address';
const lightningModalString = 'Lightning Invoice';
let loc = require('./loc');
export default class App extends React.Component { export default class App extends React.Component {
navigator = null; navigator = null;
state = {
appState: AppState.currentState,
isClipboardContentModalVisible: false,
clipboardContentModalAddressType: bitcoinModalString,
clipboardContent: '',
};
componentDidMount() { componentDidMount() {
Linking.getInitialURL() Linking.getInitialURL()
.then(url => this.handleOpenURL({ url })) .then(url => this.handleOpenURL({ url }))
.catch(console.error); .catch(console.error);
Linking.addEventListener('url', this.handleOpenURL); Linking.addEventListener('url', this.handleOpenURL);
AppState.addEventListener('change', this._handleAppStateChange);
} }
componentWillUnmount() { componentWillUnmount() {
Linking.removeEventListener('url', this.handleOpenURL); Linking.removeEventListener('url', this.handleOpenURL);
AppState.removeEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = async nextAppState => {
if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
const clipboard = await Clipboard.getString();
if (this.state.clipboardContent !== clipboard && (this.isBitcoinAddress(clipboard) || this.isLightningInvoice(clipboard))) {
this.setState({ isClipboardContentModalVisible: true });
}
this.setState({ clipboardContent: clipboard });
}
this.setState({ appState: nextAppState });
};
isBitcoinAddress(address) {
let isValidBitcoinAddress = false;
try {
bitcoin.address.toOutputScript(address);
isValidBitcoinAddress = true;
this.setState({ clipboardContentModalAddressType: bitcoinModalString });
} catch (err) {
isValidBitcoinAddress = false;
}
if (!isValidBitcoinAddress) {
if (address.indexOf('bitcoin:') === 0 || address.indexOf('BITCOIN:') === 0) {
isValidBitcoinAddress = true;
this.setState({ clipboardContentModalAddressType: bitcoinModalString });
}
}
return isValidBitcoinAddress;
}
isLightningInvoice(invoice) {
let isValidLightningInvoice = false;
if (invoice.indexOf('lightning:lnb') === 0 || invoice.indexOf('LIGHTNING:lnb') === 0 || invoice.toLowerCase().startsWith('lnb')) {
this.setState({ clipboardContentModalAddressType: lightningModalString });
isValidLightningInvoice = true;
}
return isValidLightningInvoice;
} }
handleOpenURL = event => { handleOpenURL = event => {
@ -26,7 +80,7 @@ export default class App extends React.Component {
if (typeof event.url !== 'string') { if (typeof event.url !== 'string') {
return; return;
} }
if (event.url.indexOf('bitcoin:') === 0 || event.url.indexOf('BITCOIN:') === 0) { if (this.isBitcoinAddress(event.url)) {
this.navigator && this.navigator &&
this.navigator.dispatch( this.navigator.dispatch(
NavigationActions.navigate({ NavigationActions.navigate({
@ -36,7 +90,7 @@ export default class App extends React.Component {
}, },
}), }),
); );
} else if (event.url.indexOf('lightning:') === 0 || event.url.indexOf('LIGHTNING:') === 0) { } else if (this.isLightningInvoice(event.url)) {
this.navigator && this.navigator &&
this.navigator.dispatch( this.navigator.dispatch(
NavigationActions.navigate({ NavigationActions.navigate({
@ -49,14 +103,79 @@ export default class App extends React.Component {
} }
}; };
renderClipboardContentModal = () => {
return (
<Modal
isVisible={this.state.isClipboardContentModalVisible}
style={styles.bottomModal}
onBackdropPress={() => {
this.setState({ isClipboardContentModalVisible: false });
}}
>
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'position' : null}>
<View style={styles.modalContent}>
<BlueTextCentered>
You have a {this.state.clipboardContentModalAddressType} on your clipboard. Would you like to use it for a transaction?
</BlueTextCentered>
<View style={styles.modelContentButtonLayout}>
<BlueButton
noMinWidth
title={loc.send.details.cancel}
onPress={() => this.setState({ isClipboardContentModalVisible: false })}
/>
<View style={{ marginHorizontal: 8 }} />
<BlueButton
noMinWidth
title="OK"
onPress={() => {
this.setState({ isClipboardContentModalVisible: false }, async () => {
const clipboard = await Clipboard.getString();
setTimeout(() => this.handleOpenURL({ url: clipboard }), 100);
});
}}
/>
</View>
</View>
</KeyboardAvoidingView>
</Modal>
);
};
render() { render() {
return ( return (
<View style={{ flex: 1 }}>
<MainBottomTabs <MainBottomTabs
ref={nav => { ref={nav => {
this.navigator = nav; this.navigator = nav;
NavigationService.setTopLevelNavigator(nav); NavigationService.setTopLevelNavigator(nav);
}} }}
/> />
{this.renderClipboardContentModal()}
</View>
); );
} }
} }
const styles = StyleSheet.create({
modalContent: {
backgroundColor: '#FFFFFF',
padding: 22,
justifyContent: 'center',
alignItems: 'center',
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
borderColor: 'rgba(0, 0, 0, 0.1)',
minHeight: 200,
height: 200,
},
bottomModal: {
justifyContent: 'flex-end',
margin: 0,
},
modelContentButtonLayout: {
flexDirection: 'row',
margin: 16,
justifyContent: 'space-between',
alignItems: 'flex-end',
},
});

6
BlueComponents.js

@ -51,6 +51,10 @@ export class BlueButton extends Component {
backgroundColor = '#eef0f4'; backgroundColor = '#eef0f4';
fontColor = '#9aa0aa'; fontColor = '#9aa0aa';
} }
let buttonWidth = width / 1.5;
if (this.props.hasOwnProperty('noMinWidth')) {
buttonWidth = 0;
}
return ( return (
<TouchableOpacity <TouchableOpacity
style={{ style={{
@ -62,7 +66,7 @@ export class BlueButton extends Component {
height: 45, height: 45,
maxHeight: 45, maxHeight: 45,
borderRadius: 25, borderRadius: 25,
minWidth: width / 1.5, minWidth: buttonWidth,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
}} }}

2
Privacy.js

@ -8,6 +8,6 @@ export default class Privacy {
} }
static disableBlur() { static disableBlur() {
Platform.OS === 'android' ? Obscure.disableBlur() : PrivacySnapshot.enabled(false); Platform.OS === 'android' ? Obscure.deactivateObscure() : PrivacySnapshot.enabled(false);
} }
} }

64
package-lock.json

@ -2624,11 +2624,11 @@
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
}, },
"compressible": { "compressible": {
"version": "2.0.15", "version": "2.0.16",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz",
"integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==", "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==",
"requires": { "requires": {
"mime-db": ">= 1.36.0 < 2" "mime-db": ">= 1.38.0 < 2"
} }
}, },
"compression": { "compression": {
@ -2769,13 +2769,14 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
}, },
"cosmiconfig": { "cosmiconfig": {
"version": "5.0.7", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.7.tgz", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.1.0.tgz",
"integrity": "sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA==", "integrity": "sha512-kCNPvthka8gvLtzAxQXvWo4FxqRB+ftRZyPZNuab5ngvM9Y7yw7hbEysglptLgpkGX9nAOKTBVkHUAe8xtYR6Q==",
"requires": { "requires": {
"import-fresh": "^2.0.0", "import-fresh": "^2.0.0",
"is-directory": "^0.3.1", "is-directory": "^0.3.1",
"js-yaml": "^3.9.0", "js-yaml": "^3.9.0",
"lodash.get": "^4.4.2",
"parse-json": "^4.0.0" "parse-json": "^4.0.0"
} }
}, },
@ -3944,9 +3945,9 @@
"integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="
}, },
"fbjs-scripts": { "fbjs-scripts": {
"version": "1.0.1", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/fbjs-scripts/-/fbjs-scripts-1.0.1.tgz", "resolved": "https://registry.npmjs.org/fbjs-scripts/-/fbjs-scripts-1.1.0.tgz",
"integrity": "sha512-x8bfX7k0z5B24Ue0YqjZq/2QxxaKZUNbkGdX//zbQDElMJFqBRrvRi8O3qds7UNNzs78jYqIYCS32Sk/wu5UJg==", "integrity": "sha512-VMCpHJd76YI2nYOfVM/d9LDAIFTH4uw4/7sAIGEgxk6kaNmirgTY9bLgpla9DTu+DvV2+ufvDxehGbl2U9bYCA==",
"requires": { "requires": {
"@babel/core": "^7.0.0", "@babel/core": "^7.0.0",
"ansi-colors": "^1.0.1", "ansi-colors": "^1.0.1",
@ -8212,6 +8213,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
}, },
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
},
"lodash.isempty": { "lodash.isempty": {
"version": "4.4.0", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz",
@ -8867,22 +8873,6 @@
"metro-cache": "0.49.2", "metro-cache": "0.49.2",
"metro-core": "0.49.2", "metro-core": "0.49.2",
"pretty-format": "24.0.0-alpha.6" "pretty-format": "24.0.0-alpha.6"
},
"dependencies": {
"ansi-regex": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
"integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w=="
},
"pretty-format": {
"version": "24.0.0-alpha.6",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0-alpha.6.tgz",
"integrity": "sha512-zG2m6YJeuzwBFqb5EIdmwYVf30sap+iMRuYNPytOccEXZMAJbPIFGKVJ/U0WjQegmnQbRo9CI7j6j3HtDaifiA==",
"requires": {
"ansi-regex": "^4.0.0",
"ansi-styles": "^3.2.0"
}
}
} }
}, },
"metro-core": { "metro-core": {
@ -9569,9 +9559,9 @@
} }
}, },
"on-headers": { "on-headers": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
}, },
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
@ -10778,9 +10768,9 @@
} }
}, },
"pretty-format": { "pretty-format": {
"version": "24.0.0-alpha.4", "version": "24.0.0-alpha.6",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0-alpha.4.tgz", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0-alpha.6.tgz",
"integrity": "sha512-icvbBt3XlLEVqPHdHwR2Ou9+hezS9Eccd+mA+fXfOU7T9t7ClOpq2HgCwlyw+3WogccCubKWnmzyrA/3ZZ/aOA==", "integrity": "sha512-zG2m6YJeuzwBFqb5EIdmwYVf30sap+iMRuYNPytOccEXZMAJbPIFGKVJ/U0WjQegmnQbRo9CI7j6j3HtDaifiA==",
"requires": { "requires": {
"ansi-regex": "^4.0.0", "ansi-regex": "^4.0.0",
"ansi-styles": "^3.2.0" "ansi-styles": "^3.2.0"
@ -11087,9 +11077,9 @@
} }
}, },
"react-native": { "react-native": {
"version": "0.58.1", "version": "0.58.6",
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.58.1.tgz", "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.58.6.tgz",
"integrity": "sha512-8aD0PBTney5dKQ4MBOfBEcHmdm2OBCx/9gSbeT4OUXE54fNNmDfbkVnx7EZ1iwvEdOiAl+pEpWqgAb/tvhRwBA==", "integrity": "sha512-m/7L0gYXS4yHjs+PKmyurh1LLr7/tpobAX8Iv7Dwu4XT1ZcZFeCATn420E9U3nC2XsT54AmRR2Fv7VGgf+M2vQ==",
"requires": { "requires": {
"@babel/runtime": "^7.0.0", "@babel/runtime": "^7.0.0",
"absolute-path": "^0.0.0", "absolute-path": "^0.0.0",
@ -11129,11 +11119,11 @@
"opn": "^3.0.2", "opn": "^3.0.2",
"optimist": "^0.6.1", "optimist": "^0.6.1",
"plist": "^3.0.0", "plist": "^3.0.0",
"pretty-format": "24.0.0-alpha.4", "pretty-format": "24.0.0-alpha.6",
"promise": "^7.1.1", "promise": "^7.1.1",
"prop-types": "^15.5.8", "prop-types": "^15.5.8",
"react-clone-referenced-element": "^1.0.1", "react-clone-referenced-element": "^1.0.1",
"react-devtools-core": "^3.4.0", "react-devtools-core": "^3.4.2",
"regenerator-runtime": "^0.11.0", "regenerator-runtime": "^0.11.0",
"rimraf": "^2.5.4", "rimraf": "^2.5.4",
"semver": "^5.0.3", "semver": "^5.0.3",

2
package.json

@ -62,7 +62,7 @@
"prop-types": "15.6.2", "prop-types": "15.6.2",
"react": "16.7.0", "react": "16.7.0",
"react-localization": "1.0.10", "react-localization": "1.0.10",
"react-native": "0.58.1", "react-native": "0.58.6",
"react-native-camera": "1.10.0", "react-native-camera": "1.10.0",
"react-native-device-info": "0.26.1", "react-native-device-info": "0.26.1",
"react-native-elements": "0.19.0", "react-native-elements": "0.19.0",

10
screen/send/details.js

@ -198,11 +198,11 @@ export default class SendDetails extends Component {
}; };
decodeBitcoinUri(uri) { decodeBitcoinUri(uri) {
try {
let amount = ''; let amount = '';
let parsedBitcoinUri = null; let parsedBitcoinUri = null;
let address = ''; let address = uri || '';
let memo = ''; let memo = '';
try {
parsedBitcoinUri = bip21.decode(uri); parsedBitcoinUri = bip21.decode(uri);
address = parsedBitcoinUri.hasOwnProperty('address') ? parsedBitcoinUri.address : address; address = parsedBitcoinUri.hasOwnProperty('address') ? parsedBitcoinUri.address : address;
if (parsedBitcoinUri.hasOwnProperty('options')) { if (parsedBitcoinUri.hasOwnProperty('options')) {
@ -213,10 +213,8 @@ export default class SendDetails extends Component {
memo = parsedBitcoinUri.options.label || memo; memo = parsedBitcoinUri.options.label || memo;
} }
} }
} catch (_) {}
return { address, amount, memo }; return { address, amount, memo };
} catch (_) {
return undefined;
}
} }
recalculateAvailableBalance(balance, amount, fee) { recalculateAvailableBalance(balance, amount, fee) {
@ -521,7 +519,7 @@ export default class SendDetails extends Component {
renderCreateButton = () => { renderCreateButton = () => {
return ( return (
<View style={{ paddingHorizontal: 56, paddingVertical: 16, alignContent: 'center', backgroundColor: '#FFFFFF' }}> <View style={{ marginHorizontal: 56, marginVertical: 16, alignContent: 'center', backgroundColor: '#FFFFFF', minHeight: 44 }}>
{this.state.isLoading ? ( {this.state.isLoading ? (
<ActivityIndicator /> <ActivityIndicator />
) : ( ) : (

Loading…
Cancel
Save