Merge branch 'master' into settingsui

This commit is contained in:
Marcos Rodriguez 2019-12-14 21:25:15 -05:00
commit f64ef1c71e
15 changed files with 193 additions and 157 deletions

View File

@ -163,19 +163,32 @@ export class BlueWalletNavigationHeader extends Component {
onWalletUnitChange: PropTypes.func,
};
static getDerivedStateFromProps(props, _state) {
return { wallet: props.wallet, onWalletUnitChange: props.onWalletUnitChange };
static getDerivedStateFromProps(props, state) {
return { wallet: props.wallet, onWalletUnitChange: props.onWalletUnitChange, allowOnchainAddress: state.allowOnchainAddress };
}
constructor(props) {
super(props);
this.state = { wallet: props.wallet, walletPreviousPreferredUnit: props.wallet.getPreferredBalanceUnit() };
this.state = {
wallet: props.wallet,
walletPreviousPreferredUnit: props.wallet.getPreferredBalanceUnit(),
showManageFundsButton: false,
};
}
handleCopyPress = _item => {
Clipboard.setString(loc.formatBalance(this.state.wallet.getBalance(), this.state.wallet.getPreferredBalanceUnit()).toString());
};
componentDidMount() {
if (this.state.wallet.type === LightningCustodianWallet.type) {
this.state.wallet
.allowOnchainAddress()
.then(value => this.setState({ allowOnchainAddress: value }))
.catch(e => console.log('This Lndhub wallet does not have an onchain address API.'));
}
}
handleBalanceVisibility = async _item => {
const wallet = this.state.wallet;
@ -311,7 +324,7 @@ export class BlueWalletNavigationHeader extends Component {
</Text>
)}
</TouchableOpacity>
{this.state.wallet.type === LightningCustodianWallet.type && (
{this.state.wallet.type === LightningCustodianWallet.type && this.state.allowOnchainAddress && (
<TouchableOpacity onPress={this.manageFundsPressed}>
<View
style={{
@ -1778,6 +1791,8 @@ const itemWidth = width * 0.82;
const sliderHeight = 190;
export class WalletsCarousel extends Component {
walletsCarousel = React.createRef();
constructor(props) {
super(props);
// eslint-disable-next-line
@ -1907,13 +1922,15 @@ export class WalletsCarousel extends Component {
);
}
snapToItem = item => {
this.walletsCarousel.current.snapToItem(item);
};
render() {
return (
<Carousel
{...this.props}
ref={c => {
WalletsCarousel.carousel = c;
}}
ref={this.walletsCarousel}
renderItem={this._renderItem}
sliderWidth={sliderWidth}
sliderHeight={sliderHeight}

View File

@ -119,7 +119,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "4.8.2"
versionName "4.9.0"
multiDexEnabled true
missingDimensionStrategy 'react-native-camera', 'general'
}

View File

@ -366,6 +366,15 @@ export class LightningCustodianWallet extends LegacyWallet {
return this.fetchBtcAddress();
}
async allowOnchainAddress() {
if (this.getAddress() !== undefined) {
return true;
} else {
await this.fetchBtcAddress();
return this.getAddress() !== undefined;
}
}
getTransactions() {
let txs = [];
this.pending_transactions_raw = this.pending_transactions_raw || [];

View File

@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.8.2</string>
<string>4.9.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>4.8.2</string>
<string>4.9.0</string>
<key>CFBundleVersion</key>
<string>239</string>
<key>CLKComplicationPrincipalClass</key>

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.8.2</string>
<string>4.9.0</string>
<key>CFBundleVersion</key>
<string>239</string>
<key>UISupportedInterfaceOrientations</key>

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>4.8.2</string>
<string>4.9.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>

View File

@ -1,3 +1,17 @@
v4.9.0
======
* ADD: Native segwit (BIP84) are now default wallets
* ADD: Toggle to turn off RBF when creating a transaction
* ADD: Scroll to end of wallets list when adding a wallet
* FIX: Default LN invoice expiry is now 24h instead of 1h
* FIX: Speeded up lighnint wallets
* FIX: Force Light theme mode even if system is in dark mode
* FIX: Hide Manage Funds button if wallet doesn't allow onchain refill.
* FIX: LN Scan to receive is more visible
* FIX: Quick actions not appearing on non-3d touch devices.
* FIX: Dont show clipboard modal when biometrics is dismissed
v4.8.1
======
@ -37,19 +51,3 @@ v4.7.0
* FIX: Now able to use biometrics with encrypted storage (not for unlocking)
* FIX: LApp marketplace address is now editable
* FIX: single address watch-only wallet Receive button crash
v4.6.0
======
* ADD: Optional biometrics unlock (this does not exclude full encryption)
* ADD: Cryptoadvance HW wallet support (for BIP84) via PSBT and QR codes
* ADD: LN Refill with external wallet
* ADD: Default into wallet on launch
* FIX: NaN when sending onchain
* FIX: zero on send success screen
* FIX: Time shown for top-most transaction
* FIX: minor issue with scanQrWif
* FIX: typo on NL language
* FIX: better wallet export QR readability
* FIX: RBF tx memo porting
* REF: better initial HD rescan

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "BlueWallet",
"version": "4.8.2",
"version": "4.9.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "BlueWallet",
"version": "4.8.2",
"version": "4.9.0",
"devDependencies": {
"@babel/core": "^7.5.0",
"@babel/runtime": "^7.5.1",

View File

@ -173,14 +173,14 @@ export default class WalletsAdd extends Component {
<BlueSpacing20 />
<Text style={{ color: '#0c2550', fontWeight: '500' }}>{loc.settings.advanced_options}</Text>
<RadioGroup onSelect={(index, value) => this.onSelect(index, value)} selectedIndex={0}>
<RadioButton value={HDSegwitP2SHWallet.type}>
<BlueText>{HDSegwitP2SHWallet.typeReadable} - Multiple addresses</BlueText>
<RadioButton value={HDSegwitBech32Wallet.type}>
<BlueText>{HDSegwitBech32Wallet.typeReadable} - Multiple addresses</BlueText>
</RadioButton>
<RadioButton value={SegwitP2SHWallet.type}>
<BlueText>{SegwitP2SHWallet.typeReadable} - Single address</BlueText>
</RadioButton>
<RadioButton value={HDSegwitBech32Wallet.type}>
<BlueText>{HDSegwitBech32Wallet.typeReadable} - Multiple addresses</BlueText>
<RadioButton value={HDSegwitP2SHWallet.type}>
<BlueText>{HDSegwitP2SHWallet.typeReadable} - Multiple addresses</BlueText>
</RadioButton>
</RadioGroup>
</View>
@ -220,9 +220,7 @@ export default class WalletsAdd extends Component {
title={loc.wallets.add.create}
disabled={this.state.activeBitcoin === undefined}
onPress={() => {
this.setState(
{ isLoading: true },
async () => {
this.setState({ isLoading: true }, async () => {
let w;
if (this.state.activeLightning) {
@ -290,9 +288,8 @@ export default class WalletsAdd extends Component {
this.createLightningWallet();
}
} else if (this.state.selectedIndex === 2) {
// btc was selected
// index 2 radio - hd bip84
w = new HDSegwitBech32Wallet();
// zero index radio - HD segwit
w = new HDSegwitP2SHWallet();
w.setLabel(this.state.label || loc.wallets.details.title);
} else if (this.state.selectedIndex === 1) {
// btc was selected
@ -300,8 +297,9 @@ export default class WalletsAdd extends Component {
w = new SegwitP2SHWallet();
w.setLabel(this.state.label || loc.wallets.details.title);
} else {
// zero index radio - HD segwit
w = new HDSegwitP2SHWallet();
// btc was selected
// index 2 radio - hd bip84
w = new HDSegwitBech32Wallet();
w.setLabel(this.state.label || loc.wallets.details.title);
}
if (this.state.activeBitcoin) {
@ -319,9 +317,7 @@ export default class WalletsAdd extends Component {
this.props.navigation.dismiss();
}
}
},
1,
);
});
}}
/>
) : (

View File

@ -97,6 +97,16 @@ export default class WalletsImport extends Component {
// trying other wallet types
let hd4 = new HDSegwitBech32Wallet();
hd4.setSecret(text);
if (hd4.validateMnemonic()) {
await hd4.fetchBalance();
if (hd4.getBalance() > 0) {
await hd4.fetchTransactions();
return this._saveWallet(hd4);
}
}
let segwitWallet = new SegwitP2SHWallet();
segwitWallet.setSecret(text);
if (segwitWallet.getAddress()) {
@ -130,16 +140,6 @@ export default class WalletsImport extends Component {
// if we're here - nope, its not a valid WIF
let hd4 = new HDSegwitBech32Wallet();
hd4.setSecret(text);
if (hd4.validateMnemonic()) {
await hd4.fetchBalance();
if (hd4.getBalance() > 0) {
await hd4.fetchTransactions();
return this._saveWallet(hd4);
}
}
let hd1 = new HDLegacyBreadwalletWallet();
hd1.setSecret(text);
if (hd1.validateMnemonic()) {

View File

@ -29,6 +29,8 @@ export default class WalletsList extends Component {
),
});
walletsCarousel = React.createRef();
constructor(props) {
super(props);
this.state = {
@ -37,11 +39,12 @@ export default class WalletsList extends Component {
wallets: BlueApp.getWallets().concat(false),
lastSnappedTo: 0,
};
EV(EV.enum.WALLETS_COUNT_CHANGED, this.redrawScreen.bind(this));
EV(EV.enum.WALLETS_COUNT_CHANGED, () => this.redrawScreen(true));
// here, when we receive TRANSACTIONS_COUNT_CHANGED we do not query
// remote server, we just redraw the screen
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED, this.redrawScreen.bind(this));
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED, this.redrawScreen);
}
componentDidMount() {
@ -107,7 +110,7 @@ export default class WalletsList extends Component {
);
}
redrawScreen() {
redrawScreen = (scrollToEnd = false) => {
console.log('wallets/list redrawScreen()');
if (BlueApp.getBalance() !== 0) {
A(A.ENUM.GOT_NONZERO_BALANCE);
@ -115,13 +118,25 @@ export default class WalletsList extends Component {
A(A.ENUM.GOT_ZERO_BALANCE);
}
this.setState({
const wallets = BlueApp.getWallets().concat(false);
if (scrollToEnd) {
scrollToEnd = wallets.length > this.state.wallets.length;
}
this.setState(
{
isLoading: false,
isFlatListRefreshControlHidden: true,
dataSource: BlueApp.getTransactions(null, 10),
wallets: BlueApp.getWallets().concat(false),
});
},
() => {
if (scrollToEnd) {
this.walletsCarousel.snapToItem(this.state.wallets.length - 1);
}
},
);
};
txMemo(hash) {
if (BlueApp.tx_metadata[hash] && BlueApp.tx_metadata[hash]['memo']) {
@ -268,6 +283,7 @@ export default class WalletsList extends Component {
onSnapToItem={index => {
this.onSnapToItem(index);
}}
ref={c => this.walletsCarousel = c}
/>
<BlueList>
<FlatList

View File

@ -70,8 +70,8 @@ export default class ScanQrWif extends React.Component {
}
}
// is it HD BIP84 mnemonic?
let hd = new HDSegwitBech32Wallet();
// is it HD BIP49 mnemonic?
let hd = new HDSegwitP2SHWallet();
hd.setSecret(ret.data);
if (hd.validateMnemonic()) {
for (let w of BlueApp.wallets) {
@ -90,9 +90,9 @@ export default class ScanQrWif extends React.Component {
BlueApp.wallets.push(hd);
await BlueApp.saveToDisk();
alert(loc.wallets.import.success);
this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
this.props.navigation.dismiss();
return;
}
}
@ -117,16 +117,16 @@ export default class ScanQrWif extends React.Component {
BlueApp.wallets.push(hd);
await BlueApp.saveToDisk();
alert(loc.wallets.import.success);
this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
this.props.navigation.dismiss();
return;
}
}
// nope
// is it HD mnemonic?
hd = new HDSegwitP2SHWallet();
// is it HD BIP49 mnemonic?
hd = new HDSegwitBech32Wallet();
hd.setSecret(ret.data);
if (hd.validateMnemonic()) {
for (let w of BlueApp.wallets) {
@ -144,9 +144,9 @@ export default class ScanQrWif extends React.Component {
await hd.fetchTransactions();
await BlueApp.saveToDisk();
alert(loc.wallets.import.success);
this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
this.props.navigation.dismiss();
return;
}
// nope
@ -179,11 +179,11 @@ export default class ScanQrWif extends React.Component {
BlueApp.wallets.push(lnd);
lnd.setLabel(loc.wallets.import.imported + ' ' + lnd.typeReadable);
this.props.navigation.popToTop();
alert(loc.wallets.import.success);
await BlueApp.saveToDisk();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
this.props.navigation.dismiss();
return;
}
// nope
@ -208,9 +208,9 @@ export default class ScanQrWif extends React.Component {
await watchOnly.fetchBalance();
await watchOnly.fetchTransactions();
await BlueApp.saveToDisk();
this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
this.props.navigation.dismiss();
return;
}
// nope
@ -235,8 +235,8 @@ export default class ScanQrWif extends React.Component {
await newLegacyWallet.fetchBalance();
await newLegacyWallet.fetchTransactions();
await BlueApp.saveToDisk();
this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.props.navigation.dismiss();
return;
}
@ -257,7 +257,7 @@ export default class ScanQrWif extends React.Component {
alert(loc.wallets.scanQrWif.imported_wif + ret.data + loc.wallets.scanQrWif.with_address + newWallet.getAddress());
}
await BlueApp.saveToDisk();
this.props.navigation.dismiss();
this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
}; // end
@ -337,8 +337,8 @@ export default class ScanQrWif extends React.Component {
ScanQrWif.propTypes = {
navigation: PropTypes.shape({
dismiss: PropTypes.func,
goBack: PropTypes.func,
popToTop: PropTypes.func,
navigate: PropTypes.func,
}),
};

View File

@ -52,7 +52,7 @@ describe('Bech32 Segwit HD (BIP84)', () => {
// checking that internal pointer and async address getter return the same address
let freeAddress = await hd.getAddressAsync();
assert.strictEqual(hd.next_free_address_index, 0);
assert.strictEqual(hd.next_free_address_index, 1); // someone ACTUALLY used this example mnemonic lol
assert.strictEqual(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress);
let freeChangeAddress = await hd.getChangeAddressAsync();
assert.strictEqual(hd.next_free_change_address_index, 0);