mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 19:49:22 +00:00
Cw-343-a-list-of-previously-used-addresses (#1248)
* add used addresses list * generate new address button * fix wallet type issue * fix addresses button title * update selectButton * show all wallet addresses * add tx amount and balance * fix ui * remove cashAddr format * fix generating new address issue * disable autogenerating * fix cashAddr format * minor fix * add search bar * Update address_cell.dart * fix merge conflict * address labeling feature * review fixes --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
d664f0a16f
commit
9754d67601
20 changed files with 664 additions and 330 deletions
|
@ -1,40 +1,70 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||||
|
|
||||||
class BitcoinAddressRecord {
|
class BitcoinAddressRecord {
|
||||||
BitcoinAddressRecord(this.address,
|
BitcoinAddressRecord(
|
||||||
{required this.index, this.isHidden = false, bool isUsed = false})
|
this.address, {
|
||||||
: _isUsed = isUsed;
|
required this.index,
|
||||||
|
this.isHidden = false,
|
||||||
|
int txCount = 0,
|
||||||
|
int balance = 0,
|
||||||
|
String name = '',
|
||||||
|
bool isUsed = false,
|
||||||
|
}) : _txCount = txCount,
|
||||||
|
_balance = balance,
|
||||||
|
_name = name,
|
||||||
|
_isUsed = isUsed;
|
||||||
|
|
||||||
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
|
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
|
||||||
final decoded = json.decode(jsonSource) as Map;
|
final decoded = json.decode(jsonSource) as Map;
|
||||||
|
|
||||||
return BitcoinAddressRecord(
|
return BitcoinAddressRecord(decoded['address'] as String,
|
||||||
decoded['address'] as String,
|
index: decoded['index'] as int,
|
||||||
index: decoded['index'] as int,
|
isHidden: decoded['isHidden'] as bool? ?? false,
|
||||||
isHidden: decoded['isHidden'] as bool? ?? false,
|
isUsed: decoded['isUsed'] as bool? ?? false,
|
||||||
isUsed: decoded['isUsed'] as bool? ?? false);
|
txCount: decoded['txCount'] as int? ?? 0,
|
||||||
|
name: decoded['name'] as String? ?? '',
|
||||||
|
balance: decoded['balance'] as int? ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object o) =>
|
|
||||||
o is BitcoinAddressRecord && address == o.address;
|
|
||||||
|
|
||||||
final String address;
|
final String address;
|
||||||
final bool isHidden;
|
final bool isHidden;
|
||||||
final int index;
|
final int index;
|
||||||
|
int _txCount;
|
||||||
|
int _balance;
|
||||||
|
String _name;
|
||||||
|
bool _isUsed;
|
||||||
|
|
||||||
|
int get txCount => _txCount;
|
||||||
|
|
||||||
|
String get name => _name;
|
||||||
|
|
||||||
|
int get balance => _balance;
|
||||||
|
|
||||||
|
set txCount(int value) => _txCount = value;
|
||||||
|
|
||||||
|
set balance(int value) => _balance = value;
|
||||||
|
|
||||||
bool get isUsed => _isUsed;
|
bool get isUsed => _isUsed;
|
||||||
|
|
||||||
|
void setAsUsed() => _isUsed = true;
|
||||||
|
void setNewName(String label) => _name = label;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => address.hashCode;
|
int get hashCode => address.hashCode;
|
||||||
|
|
||||||
bool _isUsed;
|
String get cashAddr => bitbox.Address.toCashAddress(address);
|
||||||
|
|
||||||
void setAsUsed() => _isUsed = true;
|
String toJSON() => json.encode({
|
||||||
|
|
||||||
String toJSON() =>
|
|
||||||
json.encode({
|
|
||||||
'address': address,
|
'address': address,
|
||||||
'index': index,
|
'index': index,
|
||||||
'isHidden': isHidden,
|
'isHidden': isHidden,
|
||||||
'isUsed': isUsed});
|
'txCount': txCount,
|
||||||
|
'name': name,
|
||||||
|
'isUsed': isUsed,
|
||||||
|
'balance': balance,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
|
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
|
||||||
.derivePath("m/0'/1"),
|
.derivePath("m/0'/1"),
|
||||||
networkType: networkType);
|
networkType: networkType);
|
||||||
|
autorun((_) {
|
||||||
|
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<BitcoinWallet> create({
|
static Future<BitcoinWallet> create({
|
||||||
|
|
|
@ -63,6 +63,7 @@ abstract class ElectrumWalletBase
|
||||||
_password = password,
|
_password = password,
|
||||||
_feeRates = <int>[],
|
_feeRates = <int>[],
|
||||||
_isTransactionUpdating = false,
|
_isTransactionUpdating = false,
|
||||||
|
isEnabledAutoGenerateSubaddress = true,
|
||||||
unspentCoins = [],
|
unspentCoins = [],
|
||||||
_scripthashesUpdateSubject = {},
|
_scripthashesUpdateSubject = {},
|
||||||
balance = ObservableMap<CryptoCurrency, ElectrumBalance>.of(currency != null
|
balance = ObservableMap<CryptoCurrency, ElectrumBalance>.of(currency != null
|
||||||
|
@ -87,6 +88,10 @@ abstract class ElectrumWalletBase
|
||||||
final bitcoin.HDWallet hd;
|
final bitcoin.HDWallet hd;
|
||||||
final String mnemonic;
|
final String mnemonic;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@observable
|
||||||
|
bool isEnabledAutoGenerateSubaddress;
|
||||||
|
|
||||||
late ElectrumClient electrumClient;
|
late ElectrumClient electrumClient;
|
||||||
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
||||||
|
|
||||||
|
@ -583,38 +588,66 @@ abstract class ElectrumWalletBase
|
||||||
Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
|
Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
|
||||||
final addressHashes = <String, BitcoinAddressRecord>{};
|
final addressHashes = <String, BitcoinAddressRecord>{};
|
||||||
final normalizedHistories = <Map<String, dynamic>>[];
|
final normalizedHistories = <Map<String, dynamic>>[];
|
||||||
|
final newTxCounts = <String, int>{};
|
||||||
|
|
||||||
walletAddresses.addresses.forEach((addressRecord) {
|
walletAddresses.addresses.forEach((addressRecord) {
|
||||||
final sh = scriptHash(addressRecord.address, networkType: networkType);
|
final sh = scriptHash(addressRecord.address, networkType: networkType);
|
||||||
addressHashes[sh] = addressRecord;
|
addressHashes[sh] = addressRecord;
|
||||||
|
newTxCounts[sh] = 0;
|
||||||
});
|
});
|
||||||
final histories = addressHashes.keys.map((scriptHash) =>
|
|
||||||
electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
|
try {
|
||||||
final historyResults = await Future.wait(histories);
|
final histories = addressHashes.keys.map((scriptHash) =>
|
||||||
historyResults.forEach((history) {
|
electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
|
||||||
history.entries.forEach((historyItem) {
|
final historyResults = await Future.wait(histories);
|
||||||
if (historyItem.value.isNotEmpty) {
|
|
||||||
final address = addressHashes[historyItem.key];
|
|
||||||
address?.setAsUsed();
|
|
||||||
normalizedHistories.addAll(historyItem.value);
|
historyResults.forEach((history) {
|
||||||
}
|
history.entries.forEach((historyItem) {
|
||||||
|
if (historyItem.value.isNotEmpty) {
|
||||||
|
final address = addressHashes[historyItem.key];
|
||||||
|
address?.setAsUsed();
|
||||||
|
newTxCounts[historyItem.key] = historyItem.value.length;
|
||||||
|
normalizedHistories.addAll(historyItem.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
final historiesWithDetails = await Future.wait(normalizedHistories.map((transaction) {
|
for (var sh in addressHashes.keys) {
|
||||||
try {
|
var balanceData = await electrumClient.getBalance(sh);
|
||||||
return fetchTransactionInfo(
|
var addressRecord = addressHashes[sh];
|
||||||
hash: transaction['tx_hash'] as String, height: transaction['height'] as int);
|
if (addressRecord != null) {
|
||||||
} catch (_) {
|
addressRecord.balance = balanceData['confirmed'] as int? ?? 0;
|
||||||
return Future.value(null);
|
}
|
||||||
}
|
}
|
||||||
}));
|
|
||||||
return historiesWithDetails
|
|
||||||
.fold<Map<String, ElectrumTransactionInfo>>(<String, ElectrumTransactionInfo>{}, (acc, tx) {
|
addressHashes.forEach((sh, addressRecord) {
|
||||||
if (tx == null) {
|
addressRecord.txCount = newTxCounts[sh] ?? 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
final historiesWithDetails = await Future.wait(normalizedHistories.map((transaction) {
|
||||||
|
try {
|
||||||
|
return fetchTransactionInfo(
|
||||||
|
hash: transaction['tx_hash'] as String, height: transaction['height'] as int);
|
||||||
|
} catch (_) {
|
||||||
|
return Future.value(null);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return historiesWithDetails.fold<Map<String, ElectrumTransactionInfo>>(
|
||||||
|
<String, ElectrumTransactionInfo>{}, (acc, tx) {
|
||||||
|
if (tx == null) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx;
|
||||||
return acc;
|
return acc;
|
||||||
}
|
});
|
||||||
acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx;
|
} catch (e) {
|
||||||
return acc;
|
print(e.toString());
|
||||||
});
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateTransactions() async {
|
Future<void> updateTransactions() async {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
|
||||||
import 'package:bitbox/bitbox.dart' as bitbox;
|
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||||
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
import 'package:cw_bitcoin/electrum.dart';
|
import 'package:cw_bitcoin/electrum.dart';
|
||||||
import 'package:cw_bitcoin/script_hash.dart';
|
import 'package:cw_bitcoin/script_hash.dart';
|
||||||
|
@ -10,8 +10,7 @@ import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
part 'electrum_wallet_addresses.g.dart';
|
part 'electrum_wallet_addresses.g.dart';
|
||||||
|
|
||||||
class ElectrumWalletAddresses = ElectrumWalletAddressesBase
|
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
|
||||||
with _$ElectrumWalletAddresses;
|
|
||||||
|
|
||||||
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
ElectrumWalletAddressesBase(WalletInfo walletInfo,
|
ElectrumWalletAddressesBase(WalletInfo walletInfo,
|
||||||
|
@ -22,19 +21,16 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
int initialRegularAddressIndex = 0,
|
int initialRegularAddressIndex = 0,
|
||||||
int initialChangeAddressIndex = 0})
|
int initialChangeAddressIndex = 0})
|
||||||
: addresses = ObservableList<BitcoinAddressRecord>.of(
|
: addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
|
||||||
(initialAddresses ?? []).toSet()),
|
receiveAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||||
receiveAddresses = ObservableList<BitcoinAddressRecord>.of(
|
|
||||||
(initialAddresses ?? [])
|
|
||||||
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed)
|
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed)
|
||||||
.toSet()),
|
.toSet()),
|
||||||
changeAddresses = ObservableList<BitcoinAddressRecord>.of(
|
changeAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||||
(initialAddresses ?? [])
|
|
||||||
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed)
|
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed)
|
||||||
.toSet()),
|
.toSet()),
|
||||||
currentReceiveAddressIndex = initialRegularAddressIndex,
|
currentReceiveAddressIndex = initialRegularAddressIndex,
|
||||||
currentChangeAddressIndex = initialChangeAddressIndex,
|
currentChangeAddressIndex = initialChangeAddressIndex,
|
||||||
super(walletInfo);
|
super(walletInfo);
|
||||||
|
|
||||||
static const defaultReceiveAddressesCount = 22;
|
static const defaultReceiveAddressesCount = 22;
|
||||||
static const defaultChangeAddressesCount = 17;
|
static const defaultChangeAddressesCount = 17;
|
||||||
|
@ -42,6 +38,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
|
||||||
static String toCashAddr(String address) => bitbox.Address.toCashAddress(address);
|
static String toCashAddr(String address) => bitbox.Address.toCashAddress(address);
|
||||||
|
|
||||||
|
static String toLegacy(String address) => bitbox.Address.toLegacyAddress(address);
|
||||||
|
|
||||||
final ObservableList<BitcoinAddressRecord> addresses;
|
final ObservableList<BitcoinAddressRecord> addresses;
|
||||||
final ObservableList<BitcoinAddressRecord> receiveAddresses;
|
final ObservableList<BitcoinAddressRecord> receiveAddresses;
|
||||||
final ObservableList<BitcoinAddressRecord> changeAddresses;
|
final ObservableList<BitcoinAddressRecord> changeAddresses;
|
||||||
|
@ -53,41 +51,67 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
@override
|
@override
|
||||||
@computed
|
@computed
|
||||||
String get address {
|
String get address {
|
||||||
if (receiveAddresses.isEmpty) {
|
if (isEnabledAutoGenerateSubaddress) {
|
||||||
final address = generateNewAddress().address;
|
if (receiveAddresses.isEmpty) {
|
||||||
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(address) : address;
|
final newAddress = generateNewAddress().address;
|
||||||
}
|
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(newAddress) : newAddress;
|
||||||
final receiveAddress = receiveAddresses.first.address;
|
}
|
||||||
|
final receiveAddress = receiveAddresses.first.address;
|
||||||
|
|
||||||
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(receiveAddress) : receiveAddress;
|
return walletInfo.type == WalletType.bitcoinCash
|
||||||
|
? toCashAddr(receiveAddress)
|
||||||
|
: receiveAddress;
|
||||||
|
} else {
|
||||||
|
final receiveAddress = (receiveAddresses.first.address != addresses.first.address &&
|
||||||
|
previousAddressRecord != null)
|
||||||
|
? previousAddressRecord!.address
|
||||||
|
: addresses.first.address;
|
||||||
|
|
||||||
|
return walletInfo.type == WalletType.bitcoinCash
|
||||||
|
? toCashAddr(receiveAddress)
|
||||||
|
: receiveAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool isEnabledAutoGenerateSubaddress = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
set address(String addr) {
|
||||||
|
if (addr.startsWith('bitcoincash:')) {
|
||||||
|
addr = toLegacy(addr);
|
||||||
|
}
|
||||||
|
final addressRecord = addresses.firstWhere((addressRecord) => addressRecord.address == addr);
|
||||||
|
|
||||||
|
previousAddressRecord = addressRecord;
|
||||||
|
receiveAddresses.remove(addressRecord);
|
||||||
|
receiveAddresses.insert(0, addressRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get primaryAddress => getAddress(index: 0, hd: mainHd);
|
String get primaryAddress => getAddress(index: 0, hd: mainHd);
|
||||||
|
|
||||||
@override
|
|
||||||
set address(String addr) => null;
|
|
||||||
|
|
||||||
int currentReceiveAddressIndex;
|
int currentReceiveAddressIndex;
|
||||||
int currentChangeAddressIndex;
|
int currentChangeAddressIndex;
|
||||||
|
|
||||||
@computed
|
@observable
|
||||||
int get totalCountOfReceiveAddresses =>
|
BitcoinAddressRecord? previousAddressRecord;
|
||||||
addresses.fold(0, (acc, addressRecord) {
|
|
||||||
if (!addressRecord.isHidden) {
|
|
||||||
return acc + 1;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
});
|
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
int get totalCountOfChangeAddresses =>
|
int get totalCountOfReceiveAddresses => addresses.fold(0, (acc, addressRecord) {
|
||||||
addresses.fold(0, (acc, addressRecord) {
|
if (!addressRecord.isHidden) {
|
||||||
if (addressRecord.isHidden) {
|
return acc + 1;
|
||||||
return acc + 1;
|
}
|
||||||
}
|
return acc;
|
||||||
return acc;
|
});
|
||||||
});
|
|
||||||
|
@computed
|
||||||
|
int get totalCountOfChangeAddresses => addresses.fold(0, (acc, addressRecord) {
|
||||||
|
if (addressRecord.isHidden) {
|
||||||
|
return acc + 1;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
});
|
||||||
|
|
||||||
Future<void> discoverAddresses() async {
|
Future<void> discoverAddresses() async {
|
||||||
await _discoverAddresses(mainHd, false);
|
await _discoverAddresses(mainHd, false);
|
||||||
|
@ -117,11 +141,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
|
||||||
if (changeAddresses.isEmpty) {
|
if (changeAddresses.isEmpty) {
|
||||||
final newAddresses = await _createNewAddresses(gap,
|
final newAddresses = await _createNewAddresses(gap,
|
||||||
hd: sideHd,
|
hd: sideHd,
|
||||||
startIndex: totalCountOfChangeAddresses > 0
|
startIndex: totalCountOfChangeAddresses > 0 ? totalCountOfChangeAddresses - 1 : 0,
|
||||||
? totalCountOfChangeAddresses - 1
|
isHidden: true);
|
||||||
: 0,
|
|
||||||
isHidden: true);
|
|
||||||
_addAddresses(newAddresses);
|
_addAddresses(newAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,14 +157,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
BitcoinAddressRecord generateNewAddress(
|
BitcoinAddressRecord generateNewAddress({bitcoin.HDWallet? hd, String? label}) {
|
||||||
{bitcoin.HDWallet? hd, bool isHidden = false}) {
|
final isHidden = hd == sideHd;
|
||||||
currentReceiveAddressIndex += 1;
|
|
||||||
// FIX-ME: Check logic for whichi HD should be used here ???
|
final newAddressIndex = addresses.fold(
|
||||||
final address = BitcoinAddressRecord(
|
0, (int acc, addressRecord) => isHidden == addressRecord.isHidden ? acc + 1 : acc);
|
||||||
getAddress(index: currentReceiveAddressIndex, hd: hd ?? sideHd),
|
|
||||||
index: currentReceiveAddressIndex,
|
final address = BitcoinAddressRecord(getAddress(index: newAddressIndex, hd: hd ?? sideHd),
|
||||||
isHidden: isHidden);
|
index: newAddressIndex, isHidden: isHidden, name: label ?? '');
|
||||||
addresses.add(address);
|
addresses.add(address);
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
@ -160,20 +182,32 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void updateAddress(String address, String label) {
|
||||||
|
if (address.startsWith('bitcoincash:')) {
|
||||||
|
address = toLegacy(address);
|
||||||
|
}
|
||||||
|
final addressRecord = addresses.firstWhere((addressRecord) => addressRecord.address == address);
|
||||||
|
addressRecord.setNewName(label);
|
||||||
|
final index = addresses.indexOf(addressRecord);
|
||||||
|
addresses.remove(addressRecord);
|
||||||
|
addresses.insert(index, addressRecord);
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void updateReceiveAddresses() {
|
void updateReceiveAddresses() {
|
||||||
receiveAddresses.removeRange(0, receiveAddresses.length);
|
receiveAddresses.removeRange(0, receiveAddresses.length);
|
||||||
final newAdresses = addresses
|
final newAddresses =
|
||||||
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
|
addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
|
||||||
receiveAddresses.addAll(newAdresses);
|
receiveAddresses.addAll(newAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void updateChangeAddresses() {
|
void updateChangeAddresses() {
|
||||||
changeAddresses.removeRange(0, changeAddresses.length);
|
changeAddresses.removeRange(0, changeAddresses.length);
|
||||||
final newAdresses = addresses
|
final newAddresses =
|
||||||
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
|
addresses.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
|
||||||
changeAddresses.addAll(newAdresses);
|
changeAddresses.addAll(newAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _discoverAddresses(bitcoin.HDWallet hd, bool isHidden) async {
|
Future<void> _discoverAddresses(bitcoin.HDWallet hd, bool isHidden) async {
|
||||||
|
@ -181,20 +215,16 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
List<BitcoinAddressRecord> addrs;
|
List<BitcoinAddressRecord> addrs;
|
||||||
|
|
||||||
if (addresses.isNotEmpty) {
|
if (addresses.isNotEmpty) {
|
||||||
addrs = addresses
|
addrs = addresses.where((addr) => addr.isHidden == isHidden).toList();
|
||||||
.where((addr) => addr.isHidden == isHidden)
|
|
||||||
.toList();
|
|
||||||
} else {
|
} else {
|
||||||
addrs = await _createNewAddresses(
|
addrs = await _createNewAddresses(
|
||||||
isHidden
|
isHidden ? defaultChangeAddressesCount : defaultReceiveAddressesCount,
|
||||||
? defaultChangeAddressesCount
|
|
||||||
: defaultReceiveAddressesCount,
|
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
hd: hd,
|
hd: hd,
|
||||||
isHidden: isHidden);
|
isHidden: isHidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
while(hasAddrUse) {
|
while (hasAddrUse) {
|
||||||
final addr = addrs.last.address;
|
final addr = addrs.last.address;
|
||||||
hasAddrUse = await _hasAddressUsed(addr);
|
hasAddrUse = await _hasAddressUsed(addr);
|
||||||
|
|
||||||
|
@ -204,11 +234,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
|
||||||
final start = addrs.length;
|
final start = addrs.length;
|
||||||
final count = start + gap;
|
final count = start + gap;
|
||||||
final batch = await _createNewAddresses(
|
final batch = await _createNewAddresses(count, startIndex: start, hd: hd, isHidden: isHidden);
|
||||||
count,
|
|
||||||
startIndex: start,
|
|
||||||
hd: hd,
|
|
||||||
isHidden: isHidden);
|
|
||||||
addrs.addAll(batch);
|
addrs.addAll(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,21 +258,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
|
||||||
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
||||||
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
||||||
final newAddresses = await _createNewAddresses(
|
final newAddresses = await _createNewAddresses(addressesCount,
|
||||||
addressesCount,
|
startIndex: countOfReceiveAddresses, hd: mainHd, isHidden: false);
|
||||||
startIndex: countOfReceiveAddresses,
|
|
||||||
hd: mainHd,
|
|
||||||
isHidden: false);
|
|
||||||
addresses.addAll(newAddresses);
|
addresses.addAll(newAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
||||||
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
||||||
final newAddresses = await _createNewAddresses(
|
final newAddresses = await _createNewAddresses(addressesCount,
|
||||||
addressesCount,
|
startIndex: countOfHiddenAddresses, hd: sideHd, isHidden: true);
|
||||||
startIndex: countOfHiddenAddresses,
|
|
||||||
hd: sideHd,
|
|
||||||
isHidden: true);
|
|
||||||
addresses.addAll(newAddresses);
|
addresses.addAll(newAddresses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,10 +276,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
final list = <BitcoinAddressRecord>[];
|
final list = <BitcoinAddressRecord>[];
|
||||||
|
|
||||||
for (var i = startIndex; i < count + startIndex; i++) {
|
for (var i = startIndex; i < count + startIndex; i++) {
|
||||||
final address = BitcoinAddressRecord(
|
final address =
|
||||||
getAddress(index: i, hd: hd),
|
BitcoinAddressRecord(getAddress(index: i, hd: hd), index: i, isHidden: isHidden);
|
||||||
index: i,
|
|
||||||
isHidden: isHidden);
|
|
||||||
list.add(address);
|
list.add(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
.fromSeed(seedBytes, network: networkType)
|
.fromSeed(seedBytes, network: networkType)
|
||||||
.derivePath("m/0'/1"),
|
.derivePath("m/0'/1"),
|
||||||
networkType: networkType,);
|
networkType: networkType,);
|
||||||
|
autorun((_) {
|
||||||
|
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LitecoinWallet> create({
|
static Future<LitecoinWallet> create({
|
||||||
|
|
|
@ -57,6 +57,9 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes)
|
sideHd: bitcoin.HDWallet.fromSeed(seedBytes)
|
||||||
.derivePath("m/44'/145'/0'/1"),
|
.derivePath("m/44'/145'/0'/1"),
|
||||||
networkType: networkType);
|
networkType: networkType);
|
||||||
|
autorun((_) {
|
||||||
|
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ class AmountConverter {
|
||||||
return _moneroAmountToString(amount);
|
return _moneroAmountToString(amount);
|
||||||
case CryptoCurrency.btc:
|
case CryptoCurrency.btc:
|
||||||
case CryptoCurrency.bch:
|
case CryptoCurrency.bch:
|
||||||
|
case CryptoCurrency.ltc:
|
||||||
return _bitcoinAmountToString(amount);
|
return _bitcoinAmountToString(amount);
|
||||||
case CryptoCurrency.xhv:
|
case CryptoCurrency.xhv:
|
||||||
case CryptoCurrency.xag:
|
case CryptoCurrency.xag:
|
||||||
|
|
|
@ -63,9 +63,17 @@ class CWBitcoin extends Bitcoin {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> generateNewAddress(Object wallet) async {
|
Future<void> generateNewAddress(Object wallet, String label) async {
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
await bitcoinWallet.walletAddresses.generateNewAddress();
|
await bitcoinWallet.walletAddresses.generateNewAddress(label: label);
|
||||||
|
await wallet.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateAddress(Object wallet,String address, String label) async {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
bitcoinWallet.walletAddresses.updateAddress(address, label);
|
||||||
|
await wallet.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -99,6 +107,21 @@ class CWBitcoin extends Bitcoin {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@computed
|
||||||
|
List<ElectrumSubAddress> getSubAddresses(Object wallet) {
|
||||||
|
final electrumWallet = wallet as ElectrumWallet;
|
||||||
|
return electrumWallet.walletAddresses.addresses
|
||||||
|
.map((BitcoinAddressRecord addr) => ElectrumSubAddress(
|
||||||
|
id: addr.index,
|
||||||
|
name: addr.name,
|
||||||
|
address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address,
|
||||||
|
txCount: addr.txCount,
|
||||||
|
balance: addr.balance,
|
||||||
|
isChange: addr.isHidden))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAddress(Object wallet) {
|
String getAddress(Object wallet) {
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
|
|
@ -68,7 +68,8 @@ void startCurrentWalletChangeReaction(
|
||||||
.get<SharedPreferences>()
|
.get<SharedPreferences>()
|
||||||
.setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
.setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero || wallet.type == WalletType.bitcoin ||
|
||||||
|
wallet.type == WalletType.litecoin || wallet.type == WalletType.bitcoinCash ) {
|
||||||
_setAutoGenerateSubaddressStatus(wallet, settingsStore);
|
_setAutoGenerateSubaddressStatus(wallet, settingsStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
@ -15,6 +16,7 @@ import 'package:cake_wallet/utils/share_util.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
||||||
|
@ -155,63 +157,27 @@ class AddressPage extends BasePage {
|
||||||
amountController: _amountController,
|
amountController: _amountController,
|
||||||
isLight: dashboardViewModel.settingsStore.currentTheme.type ==
|
isLight: dashboardViewModel.settingsStore.currentTheme.type ==
|
||||||
ThemeType.light))),
|
ThemeType.light))),
|
||||||
|
SizedBox(height: 16),
|
||||||
Observer(builder: (_) {
|
Observer(builder: (_) {
|
||||||
if (addressListViewModel.hasAddressList) {
|
if (addressListViewModel.hasAddressList) {
|
||||||
return GestureDetector(
|
return SelectButton(
|
||||||
onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled
|
text: addressListViewModel.buttonTitle,
|
||||||
|
onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled &&
|
||||||
|
(WalletType.monero == addressListViewModel.wallet.type ||
|
||||||
|
WalletType.haven == addressListViewModel.wallet.type)
|
||||||
? await showPopUp<void>(
|
? await showPopUp<void>(
|
||||||
context: context, builder: (_) => getIt.get<MoneroAccountListPage>())
|
context: context,
|
||||||
|
builder: (_) => getIt.get<MoneroAccountListPage>())
|
||||||
: Navigator.of(context).pushNamed(Routes.receive),
|
: Navigator.of(context).pushNamed(Routes.receive),
|
||||||
child: Container(
|
textColor: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
||||||
height: 50,
|
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||||
padding: EdgeInsets.only(left: 24, right: 12),
|
borderColor: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
||||||
alignment: Alignment.center,
|
arrowColor: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
||||||
decoration: BoxDecoration(
|
textSize: 14,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
height: 50,
|
||||||
border: Border.all(
|
|
||||||
color:
|
|
||||||
Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
|
||||||
width: 1),
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<SyncIndicatorTheme>()!
|
|
||||||
.syncedBackgroundColor),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
|
||||||
Observer(
|
|
||||||
builder: (_) {
|
|
||||||
String label = addressListViewModel.hasAccounts
|
|
||||||
? S.of(context).accounts_subaddresses
|
|
||||||
: S.of(context).addresses;
|
|
||||||
|
|
||||||
if (dashboardViewModel.isAutoGenerateSubaddressesEnabled) {
|
|
||||||
label = addressListViewModel.hasAccounts
|
|
||||||
? S.of(context).accounts
|
|
||||||
: S.of(context).account;
|
|
||||||
}
|
|
||||||
return Text(
|
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<SyncIndicatorTheme>()!
|
|
||||||
.textColor),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
size: 14,
|
|
||||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
} else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled ||
|
} else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled ||
|
||||||
addressListViewModel.showElectrumAddressDisclaimer) {
|
addressListViewModel.isElectrumWallet) {
|
||||||
return Text(S.of(context).electrum_address_disclaimer,
|
return Text(S.of(context).electrum_address_disclaimer,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
|
|
@ -11,29 +11,37 @@ class SelectButton extends StatelessWidget {
|
||||||
this.isSelected = false,
|
this.isSelected = false,
|
||||||
this.showTrailingIcon = true,
|
this.showTrailingIcon = true,
|
||||||
this.height = 60,
|
this.height = 60,
|
||||||
|
this.textSize = 18,
|
||||||
|
this.color,
|
||||||
|
this.textColor,
|
||||||
|
this.arrowColor,
|
||||||
|
this.borderColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Image? image;
|
final Image? image;
|
||||||
final String text;
|
final String text;
|
||||||
|
final double textSize;
|
||||||
final bool isSelected;
|
final bool isSelected;
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
final bool showTrailingIcon;
|
final bool showTrailingIcon;
|
||||||
final double height;
|
final double height;
|
||||||
|
final Color? color;
|
||||||
|
final Color? textColor;
|
||||||
|
final Color? arrowColor;
|
||||||
|
final Color? borderColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final color = isSelected
|
final backgroundColor = color ?? (isSelected ? Colors.green : Theme.of(context).cardColor);
|
||||||
? Colors.green
|
final effectiveTextColor = textColor ?? (isSelected
|
||||||
: Theme.of(context).cardColor;
|
|
||||||
final textColor = isSelected
|
|
||||||
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
||||||
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
|
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor);
|
||||||
final arrowColor = isSelected
|
final effectiveArrowColor = arrowColor ?? (isSelected
|
||||||
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
||||||
: Theme.of(context).extension<FilterTheme>()!.titlesColor;
|
: Theme.of(context).extension<FilterTheme>()!.titlesColor);
|
||||||
|
|
||||||
final selectArrowImage = Image.asset('assets/images/select_arrow.png',
|
final selectArrowImage = Image.asset('assets/images/select_arrow.png',
|
||||||
color: arrowColor);
|
color: effectiveArrowColor);
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
@ -44,7 +52,9 @@ class SelectButton extends StatelessWidget {
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||||
color: color
|
color: backgroundColor,
|
||||||
|
border: borderColor != null ? Border.all(color: borderColor!) : null,
|
||||||
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
@ -63,9 +73,9 @@ class SelectButton extends StatelessWidget {
|
||||||
child: Text(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18,
|
fontSize: textSize,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: textColor
|
color: effectiveTextColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -49,7 +49,7 @@ class ReceivePage extends BasePage {
|
||||||
bool get gradientBackground => true;
|
bool get gradientBackground => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get resizeToAvoidBottomInset => false;
|
bool get resizeToAvoidBottomInset => true;
|
||||||
|
|
||||||
final FocusNode _cryptoAmountFocus;
|
final FocusNode _cryptoAmountFocus;
|
||||||
|
|
||||||
|
@ -99,10 +99,11 @@ class ReceivePage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
|
final isElectrumWallet = addressListViewModel.isElectrumWallet;
|
||||||
return (addressListViewModel.type == WalletType.monero ||
|
return (addressListViewModel.type == WalletType.monero ||
|
||||||
addressListViewModel.type == WalletType.haven ||
|
addressListViewModel.type == WalletType.haven ||
|
||||||
addressListViewModel.type == WalletType.nano ||
|
addressListViewModel.type == WalletType.nano ||
|
||||||
addressListViewModel.type == WalletType.banano)
|
isElectrumWallet)
|
||||||
? KeyboardActions(
|
? KeyboardActions(
|
||||||
config: KeyboardActionsConfig(
|
config: KeyboardActionsConfig(
|
||||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||||
|
@ -140,7 +141,9 @@ class ReceivePage extends BasePage {
|
||||||
|
|
||||||
if (item is WalletAccountListHeader) {
|
if (item is WalletAccountListHeader) {
|
||||||
cell = HeaderTile(
|
cell = HeaderTile(
|
||||||
onTap: () async {
|
showTrailingButton: true,
|
||||||
|
walletAddressListViewModel: addressListViewModel,
|
||||||
|
trailingButtonTap: () async {
|
||||||
if (addressListViewModel.type == WalletType.monero ||
|
if (addressListViewModel.type == WalletType.monero ||
|
||||||
addressListViewModel.type == WalletType.haven) {
|
addressListViewModel.type == WalletType.haven) {
|
||||||
await showPopUp<void>(
|
await showPopUp<void>(
|
||||||
|
@ -153,7 +156,7 @@ class ReceivePage extends BasePage {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title: S.of(context).accounts,
|
title: S.of(context).accounts,
|
||||||
icon: Icon(
|
trailingIcon: Icon(
|
||||||
Icons.arrow_forward_ios,
|
Icons.arrow_forward_ios,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||||
|
@ -161,16 +164,21 @@ class ReceivePage extends BasePage {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item is WalletAddressListHeader) {
|
if (item is WalletAddressListHeader) {
|
||||||
cell = HeaderTile(
|
cell = HeaderTile(
|
||||||
onTap: () =>
|
title: S.of(context).addresses,
|
||||||
Navigator.of(context).pushNamed(Routes.newSubaddress),
|
walletAddressListViewModel: addressListViewModel,
|
||||||
title: S.of(context).addresses,
|
showTrailingButton: !addressListViewModel.isAutoGenerateSubaddressEnabled,
|
||||||
icon: Icon(
|
showSearchButton: true,
|
||||||
Icons.add,
|
trailingButtonTap: () =>
|
||||||
size: 20,
|
Navigator.of(context).pushNamed(Routes.newSubaddress),
|
||||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
trailingIcon: Icon(
|
||||||
));
|
Icons.add,
|
||||||
}
|
size: 20,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<ReceivePageTheme>()!
|
||||||
|
.iconsColor,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if (item is WalletAddressListItem) {
|
if (item is WalletAddressListItem) {
|
||||||
cell = Observer(builder: (_) {
|
cell = Observer(builder: (_) {
|
||||||
|
@ -185,6 +193,7 @@ class ReceivePage extends BasePage {
|
||||||
|
|
||||||
return AddressCell.fromItem(item,
|
return AddressCell.fromItem(item,
|
||||||
isCurrent: isCurrent,
|
isCurrent: isCurrent,
|
||||||
|
hasBalance: addressListViewModel.isElectrumWallet,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
textColor: textColor,
|
textColor: textColor,
|
||||||
onTap: (_) => addressListViewModel.setAddress(item),
|
onTap: (_) => addressListViewModel.setAddress(item),
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
|
||||||
class AddressCell extends StatelessWidget {
|
class AddressCell extends StatelessWidget {
|
||||||
AddressCell(
|
AddressCell(
|
||||||
|
@ -12,13 +13,18 @@ class AddressCell extends StatelessWidget {
|
||||||
required this.backgroundColor,
|
required this.backgroundColor,
|
||||||
required this.textColor,
|
required this.textColor,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.onEdit});
|
this.onEdit,
|
||||||
|
this.txCount,
|
||||||
|
this.balance,
|
||||||
|
this.isChange = false,
|
||||||
|
this.hasBalance = false});
|
||||||
|
|
||||||
factory AddressCell.fromItem(WalletAddressListItem item,
|
factory AddressCell.fromItem(WalletAddressListItem item,
|
||||||
{required bool isCurrent,
|
{required bool isCurrent,
|
||||||
required Color backgroundColor,
|
required Color backgroundColor,
|
||||||
required Color textColor,
|
required Color textColor,
|
||||||
Function(String)? onTap,
|
Function(String)? onTap,
|
||||||
|
bool hasBalance = false,
|
||||||
Function()? onEdit}) =>
|
Function()? onEdit}) =>
|
||||||
AddressCell(
|
AddressCell(
|
||||||
address: item.address,
|
address: item.address,
|
||||||
|
@ -28,7 +34,11 @@ class AddressCell extends StatelessWidget {
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
textColor: textColor,
|
textColor: textColor,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onEdit: onEdit);
|
onEdit: onEdit,
|
||||||
|
txCount: item.txCount,
|
||||||
|
balance: item.balance,
|
||||||
|
isChange: item.isChange,
|
||||||
|
hasBalance: hasBalance);
|
||||||
|
|
||||||
final String address;
|
final String address;
|
||||||
final String name;
|
final String name;
|
||||||
|
@ -38,17 +48,22 @@ class AddressCell extends StatelessWidget {
|
||||||
final Color textColor;
|
final Color textColor;
|
||||||
final Function(String)? onTap;
|
final Function(String)? onTap;
|
||||||
final Function()? onEdit;
|
final Function()? onEdit;
|
||||||
|
final int? txCount;
|
||||||
|
final String? balance;
|
||||||
|
final bool isChange;
|
||||||
|
final bool hasBalance;
|
||||||
|
|
||||||
String get label {
|
static const int addressPreviewLength = 8;
|
||||||
if (name.isEmpty){
|
|
||||||
if(address.length<=16){
|
String get formattedAddress {
|
||||||
return address;
|
final formatIfCashAddr = address.replaceAll('bitcoincash:', '');
|
||||||
}else{
|
|
||||||
return address.substring(0,8)+'...'+
|
if (formatIfCashAddr.length <= (name.isNotEmpty ? 16 : 43)) {
|
||||||
address.substring(address.length-8,address.length);
|
return formatIfCashAddr;
|
||||||
}
|
} else {
|
||||||
}else{
|
return formatIfCashAddr.substring(0, addressPreviewLength) +
|
||||||
return name;
|
'...' +
|
||||||
|
formatIfCashAddr.substring(formatIfCashAddr.length - addressPreviewLength, formatIfCashAddr.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,41 +74,114 @@ class AddressCell extends StatelessWidget {
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
padding: EdgeInsets.only(left: 24, right: 24, top: 28, bottom: 28),
|
padding: EdgeInsets.only(left: 24, right: 24, top: 20, bottom: 20),
|
||||||
child: Text(
|
child: Row(
|
||||||
label,
|
children: [
|
||||||
maxLines: 1,
|
Expanded(
|
||||||
overflow: TextOverflow.ellipsis,
|
child: Column(
|
||||||
style: TextStyle(
|
children: [
|
||||||
fontSize: 14,
|
Row(
|
||||||
color: textColor,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
),
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
if (isChange)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
|
child: Container(
|
||||||
|
height: 20,
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8.5)),
|
||||||
|
color: textColor),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
S.of(context).unspent_change,
|
||||||
|
style: TextStyle(
|
||||||
|
color: backgroundColor,
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (name.isNotEmpty)
|
||||||
|
Text(
|
||||||
|
'$name - ',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AutoSizeText(
|
||||||
|
formattedAddress,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isChange ? 10 : 14,
|
||||||
|
color: textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (hasBalance)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Balance: $balance',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${S.of(context).transactions.toLowerCase()}: $txCount',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
return Semantics(
|
return onEdit == null
|
||||||
label: S.of(context).slidable,
|
? cell
|
||||||
selected: isCurrent,
|
: Semantics(
|
||||||
enabled: !isCurrent,
|
label: S.of(context).slidable,
|
||||||
child: Slidable(
|
selected: isCurrent,
|
||||||
key: Key(address),
|
enabled: !isCurrent,
|
||||||
startActionPane: _actionPane(context),
|
child: Slidable(
|
||||||
endActionPane: _actionPane(context),
|
key: Key(address),
|
||||||
child: cell,
|
startActionPane: _actionPane(context),
|
||||||
),
|
endActionPane: _actionPane(context),
|
||||||
);
|
child: cell,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionPane _actionPane(BuildContext context) => ActionPane(
|
ActionPane _actionPane(BuildContext context) => ActionPane(
|
||||||
motion: const ScrollMotion(),
|
motion: const ScrollMotion(),
|
||||||
extentRatio: 0.3,
|
extentRatio: 0.3,
|
||||||
children: [
|
children: [
|
||||||
SlidableAction(
|
SlidableAction(
|
||||||
onPressed: (_) => onEdit?.call(),
|
onPressed: (_) => onEdit?.call(),
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
icon: Icons.edit,
|
icon: Icons.edit,
|
||||||
label: S.of(context).edit,
|
label: S.of(context).edit,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +1,114 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HeaderTile extends StatelessWidget {
|
class HeaderTile extends StatefulWidget {
|
||||||
HeaderTile({
|
HeaderTile({
|
||||||
required this.onTap,
|
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.icon
|
required this.walletAddressListViewModel,
|
||||||
|
this.showSearchButton = false,
|
||||||
|
this.showTrailingButton = false,
|
||||||
|
this.trailingButtonTap,
|
||||||
|
this.trailingIcon,
|
||||||
});
|
});
|
||||||
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final String title;
|
final String title;
|
||||||
final Icon icon;
|
final WalletAddressListViewModel walletAddressListViewModel;
|
||||||
|
final bool showSearchButton;
|
||||||
|
final bool showTrailingButton;
|
||||||
|
final VoidCallback? trailingButtonTap;
|
||||||
|
final Icon? trailingIcon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_HeaderTileState createState() => _HeaderTileState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HeaderTileState extends State<HeaderTile> {
|
||||||
|
bool _isSearchActive = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
final searchIcon = Image.asset("assets/images/search_icon.png",
|
||||||
onTap: onTap,
|
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor);
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.only(
|
return Container(
|
||||||
left: 24,
|
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||||
right: 24,
|
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor,
|
||||||
top: 24,
|
child: Row(
|
||||||
bottom: 24
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
),
|
children: <Widget>[
|
||||||
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor,
|
_isSearchActive
|
||||||
child: Row(
|
? Expanded(
|
||||||
mainAxisSize: MainAxisSize.max,
|
child: TextField(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
onChanged: (value) => widget.walletAddressListViewModel.updateSearchText(value),
|
||||||
children: <Widget>[
|
cursorColor: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
|
||||||
Text(
|
cursorWidth: 0.5,
|
||||||
title,
|
decoration: InputDecoration(
|
||||||
style: TextStyle(
|
hintText: '${S.of(context).search}...',
|
||||||
fontSize: 18,
|
isDense: true,
|
||||||
fontWeight: FontWeight.w600,
|
contentPadding: EdgeInsets.zero,
|
||||||
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
|
hintStyle: TextStyle(
|
||||||
),
|
fontSize: 16,
|
||||||
Container(
|
fontWeight: FontWeight.w600,
|
||||||
height: 32,
|
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
|
||||||
width: 32,
|
border: UnderlineInputBorder(
|
||||||
decoration: BoxDecoration(
|
borderSide: BorderSide(color: Theme.of(context).dividerColor),
|
||||||
shape: BoxShape.circle,
|
),
|
||||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsBackgroundColor),
|
focusedBorder: UnderlineInputBorder(
|
||||||
child: icon,
|
borderSide: BorderSide(color: Theme.of(context).dividerColor),
|
||||||
)
|
),
|
||||||
],
|
enabledBorder: UnderlineInputBorder(
|
||||||
),
|
borderSide: BorderSide(color: Theme.of(context).dividerColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
autofocus: true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
widget.title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
if (widget.showSearchButton)
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_isSearchActive = !_isSearchActive;
|
||||||
|
widget.walletAddressListViewModel.updateSearchText('');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<ReceivePageTheme>()!
|
||||||
|
.iconsBackgroundColor),
|
||||||
|
child: searchIcon,
|
||||||
|
)),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
if (widget.showTrailingButton)
|
||||||
|
GestureDetector(
|
||||||
|
onTap: widget.trailingButtonTap,
|
||||||
|
child: Container(
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color:
|
||||||
|
Theme.of(context).extension<ReceivePageTheme>()!.iconsBackgroundColor),
|
||||||
|
child: widget.trailingIcon,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
|
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
@ -6,6 +7,7 @@ import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dar
|
||||||
import 'package:cake_wallet/utils/brightness_util.dart';
|
import 'package:cake_wallet/utils/brightness_util.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
@ -144,9 +146,10 @@ class QRWidget extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: AutoSizeText(
|
||||||
addressListViewModel.address.address,
|
addressListViewModel.address.address,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: addressListViewModel.wallet.type == WalletType.monero ? 2 : 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
|
|
|
@ -38,7 +38,11 @@ abstract class PrivacySettingsViewModelBase with Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isAutoGenerateSubaddressesVisible => _wallet.type == WalletType.monero;
|
bool get isAutoGenerateSubaddressesVisible =>
|
||||||
|
_wallet.type == WalletType.monero ||
|
||||||
|
_wallet.type == WalletType.bitcoin ||
|
||||||
|
_wallet.type == WalletType.litecoin ||
|
||||||
|
_wallet.type == WalletType.bitcoinCash;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress;
|
bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
import 'package:cake_wallet/monero/monero.dart';
|
import 'package:cake_wallet/monero/monero.dart';
|
||||||
|
@ -33,7 +32,7 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||||
state = AddressEditOrCreateStateInitial(),
|
state = AddressEditOrCreateStateInitial(),
|
||||||
label = item?.name ?? '',
|
label = item?.name ?? '',
|
||||||
_item = item,
|
_item = item,
|
||||||
_wallet = wallet;
|
_wallet = wallet;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
AddressEditOrCreateState state;
|
AddressEditOrCreateState state;
|
||||||
|
@ -46,6 +45,10 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||||
final WalletAddressListItem? _item;
|
final WalletAddressListItem? _item;
|
||||||
final WalletBase _wallet;
|
final WalletBase _wallet;
|
||||||
|
|
||||||
|
bool get isElectrum => _wallet.type == WalletType.bitcoin ||
|
||||||
|
_wallet.type == WalletType.bitcoinCash ||
|
||||||
|
_wallet.type == WalletType.litecoin;
|
||||||
|
|
||||||
Future<void> save() async {
|
Future<void> save() async {
|
||||||
try {
|
try {
|
||||||
state = AddressIsSaving();
|
state = AddressIsSaving();
|
||||||
|
@ -65,12 +68,7 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||||
Future<void> _createNew() async {
|
Future<void> _createNew() async {
|
||||||
final wallet = _wallet;
|
final wallet = _wallet;
|
||||||
|
|
||||||
if (wallet.type == WalletType.bitcoin
|
if (isElectrum) await bitcoin!.generateNewAddress(wallet, label);
|
||||||
|| wallet.type == WalletType.litecoin
|
|
||||||
|| wallet.type == WalletType.bitcoinCash) {
|
|
||||||
await bitcoin!.generateNewAddress(wallet);
|
|
||||||
await wallet.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero) {
|
||||||
await monero
|
await monero
|
||||||
|
@ -96,10 +94,8 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||||
Future<void> _update() async {
|
Future<void> _update() async {
|
||||||
final wallet = _wallet;
|
final wallet = _wallet;
|
||||||
|
|
||||||
/*if (wallet is BitcoinWallet) {
|
if (isElectrum) await bitcoin!.updateAddress(wallet, _item!.address, label);
|
||||||
await wallet.walletAddresses.updateAddress(_item.address as String);
|
|
||||||
await wallet.save();
|
|
||||||
}*/
|
|
||||||
final index = _item?.id;
|
final index = _item?.id;
|
||||||
if (index != null) {
|
if (index != null) {
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero) {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:cake_wallet/utils/list_item.dart';
|
import 'package:cake_wallet/utils/list_item.dart';
|
||||||
|
|
||||||
class WalletAddressListItem extends ListItem {
|
class WalletAddressListItem extends ListItem {
|
||||||
|
@ -6,13 +5,19 @@ class WalletAddressListItem extends ListItem {
|
||||||
required this.address,
|
required this.address,
|
||||||
required this.isPrimary,
|
required this.isPrimary,
|
||||||
this.id,
|
this.id,
|
||||||
this.name})
|
this.name,
|
||||||
|
this.txCount,
|
||||||
|
this.balance,
|
||||||
|
this.isChange = false})
|
||||||
: super();
|
: super();
|
||||||
|
|
||||||
final int? id;
|
final int? id;
|
||||||
final bool isPrimary;
|
final bool isPrimary;
|
||||||
final String address;
|
final String address;
|
||||||
final String? name;
|
final String? name;
|
||||||
|
final int? txCount;
|
||||||
|
final String? balance;
|
||||||
|
final bool isChange;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => name ?? address;
|
String toString() => name ?? address;
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/haven/haven.dart';
|
||||||
|
import 'package:cake_wallet/monero/monero.dart';
|
||||||
import 'package:cake_wallet/polygon/polygon.dart';
|
import 'package:cake_wallet/polygon/polygon.dart';
|
||||||
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||||
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||||
import 'package:cw_core/currency.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:mobx/mobx.dart';
|
|
||||||
import 'package:cake_wallet/utils/list_item.dart';
|
import 'package:cake_wallet/utils/list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||||
|
import 'package:cw_core/amount_converter.dart';
|
||||||
|
import 'package:cw_core/currency.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:cake_wallet/store/app_store.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:cake_wallet/monero/monero.dart';
|
|
||||||
import 'package:cake_wallet/haven/haven.dart';
|
|
||||||
|
|
||||||
part 'wallet_address_list_view_model.g.dart';
|
part 'wallet_address_list_view_model.g.dart';
|
||||||
|
|
||||||
|
@ -110,7 +114,8 @@ class EthereumURI extends PaymentURI {
|
||||||
|
|
||||||
class BitcoinCashURI extends PaymentURI {
|
class BitcoinCashURI extends PaymentURI {
|
||||||
BitcoinCashURI({required String amount, required String address})
|
BitcoinCashURI({required String amount, required String address})
|
||||||
: super(amount: amount, address: address);
|
: super(amount: amount, address: address);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
var base = address;
|
var base = address;
|
||||||
|
@ -121,9 +126,7 @@ class BitcoinCashURI extends PaymentURI {
|
||||||
|
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class NanoURI extends PaymentURI {
|
class NanoURI extends PaymentURI {
|
||||||
NanoURI({required String amount, required String address})
|
NanoURI({required String amount, required String address})
|
||||||
|
@ -167,6 +170,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
hasAccounts =
|
hasAccounts =
|
||||||
appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven,
|
appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven,
|
||||||
amount = '',
|
amount = '',
|
||||||
|
_settingsStore = appStore.settingsStore,
|
||||||
super(appStore: appStore) {
|
super(appStore: appStore) {
|
||||||
_init();
|
_init();
|
||||||
}
|
}
|
||||||
|
@ -184,12 +188,28 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
final NumberFormat _cryptoNumberFormat;
|
final NumberFormat _cryptoNumberFormat;
|
||||||
|
|
||||||
final FiatConversionStore fiatConversionStore;
|
final FiatConversionStore fiatConversionStore;
|
||||||
|
final SettingsStore _settingsStore;
|
||||||
|
|
||||||
List<Currency> get currencies => [walletTypeToCryptoCurrency(wallet.type), ...FiatCurrency.all];
|
List<Currency> get currencies => [walletTypeToCryptoCurrency(wallet.type), ...FiatCurrency.all];
|
||||||
|
|
||||||
|
String get buttonTitle {
|
||||||
|
if (isElectrumWallet) {
|
||||||
|
return S.current.addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAutoGenerateSubaddressEnabled) {
|
||||||
|
return hasAccounts ? S.current.accounts : S.current.account;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasAccounts ? S.current.accounts_subaddresses : S.current.addresses;
|
||||||
|
}
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
Currency selectedCurrency;
|
Currency selectedCurrency;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
String searchText = '';
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
int get selectedCurrencyIndex => currencies.indexOf(selectedCurrency);
|
int get selectedCurrencyIndex => currencies.indexOf(selectedCurrency);
|
||||||
|
|
||||||
|
@ -277,14 +297,21 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
addressList.addAll(addressItems);
|
addressList.addAll(addressItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wallet.type == WalletType.bitcoin) {
|
if (isElectrumWallet) {
|
||||||
final primaryAddress = bitcoin!.getAddress(wallet);
|
final addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) {
|
||||||
final bitcoinAddresses = bitcoin!.getAddresses(wallet).map((addr) {
|
final isPrimary = subaddress.id == 0;
|
||||||
final isPrimary = addr == primaryAddress;
|
|
||||||
|
|
||||||
return WalletAddressListItem(isPrimary: isPrimary, name: null, address: addr);
|
return WalletAddressListItem(
|
||||||
|
id: subaddress.id,
|
||||||
|
isPrimary: isPrimary,
|
||||||
|
name: subaddress.name,
|
||||||
|
address: subaddress.address,
|
||||||
|
txCount: subaddress.txCount,
|
||||||
|
balance: AmountConverter.amountIntToString(
|
||||||
|
walletTypeToCryptoCurrency(type), subaddress.balance),
|
||||||
|
isChange: subaddress.isChange);
|
||||||
});
|
});
|
||||||
addressList.addAll(bitcoinAddresses);
|
addressList.addAll(addressItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wallet.type == WalletType.ethereum) {
|
if (wallet.type == WalletType.ethereum) {
|
||||||
|
@ -299,6 +326,15 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (searchText.isNotEmpty) {
|
||||||
|
return ObservableList.of(addressList.where((item) {
|
||||||
|
if (item is WalletAddressListItem) {
|
||||||
|
return item.address.toLowerCase().contains(searchText.toLowerCase());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
return addressList;
|
return addressList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,15 +357,23 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
@computed
|
@computed
|
||||||
bool get hasAddressList =>
|
bool get hasAddressList =>
|
||||||
wallet.type == WalletType.monero ||
|
wallet.type == WalletType.monero ||
|
||||||
wallet.type == WalletType.haven;/* ||
|
wallet.type == WalletType.haven ||
|
||||||
wallet.type == WalletType.nano ||
|
wallet.type == WalletType.bitcoinCash ||
|
||||||
wallet.type == WalletType.banano;*/// TODO: nano accounts are disabled for now
|
wallet.type == WalletType.bitcoin ||
|
||||||
|
wallet.type == WalletType.litecoin;
|
||||||
|
|
||||||
|
// wallet.type == WalletType.nano ||
|
||||||
|
// wallet.type == WalletType.banano; TODO: nano accounts are disabled for now
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get showElectrumAddressDisclaimer =>
|
bool get isElectrumWallet =>
|
||||||
wallet.type == WalletType.bitcoin ||
|
wallet.type == WalletType.bitcoin ||
|
||||||
wallet.type == WalletType.litecoin ||
|
wallet.type == WalletType.litecoin ||
|
||||||
wallet.type == WalletType.bitcoinCash;
|
wallet.type == WalletType.bitcoinCash;
|
||||||
|
|
||||||
|
@computed
|
||||||
|
bool get isAutoGenerateSubaddressEnabled =>
|
||||||
|
_settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled;
|
||||||
|
|
||||||
List<ListItem> _baseItems;
|
List<ListItem> _baseItems;
|
||||||
|
|
||||||
|
@ -343,9 +387,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
_baseItems = [];
|
_baseItems = [];
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero ||
|
if (wallet.type == WalletType.monero ||
|
||||||
wallet.type == WalletType.haven /*||
|
wallet.type ==
|
||||||
|
WalletType
|
||||||
|
.haven /*||
|
||||||
wallet.type == WalletType.nano ||
|
wallet.type == WalletType.nano ||
|
||||||
wallet.type == WalletType.banano*/) {
|
wallet.type == WalletType.banano*/
|
||||||
|
) {
|
||||||
_baseItems.add(WalletAccountListHeader());
|
_baseItems.add(WalletAccountListHeader());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,6 +414,11 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void updateSearchText(String text) {
|
||||||
|
searchText = text;
|
||||||
|
}
|
||||||
|
|
||||||
void _convertAmountToCrypto() {
|
void _convertAmountToCrypto() {
|
||||||
final cryptoCurrency = walletTypeToCryptoCurrency(wallet.type);
|
final cryptoCurrency = walletTypeToCryptoCurrency(wallet.type);
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -64,6 +64,7 @@ import 'package:cw_core/output_info.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:cw_core/wallet_service.dart';
|
import 'package:cw_core/wallet_service.dart';
|
||||||
import 'package:cake_wallet/view_model/send/output.dart';
|
import 'package:cake_wallet/view_model/send/output.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:hive/hive.dart';""";
|
import 'package:hive/hive.dart';""";
|
||||||
const bitcoinCWHeaders = """
|
const bitcoinCWHeaders = """
|
||||||
import 'package:cw_bitcoin/electrum_wallet.dart';
|
import 'package:cw_bitcoin/electrum_wallet.dart';
|
||||||
|
@ -76,9 +77,27 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||||
import 'package:cw_bitcoin/litecoin_wallet_service.dart';
|
import 'package:cw_bitcoin/litecoin_wallet_service.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
""";
|
""";
|
||||||
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
|
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
|
||||||
const bitcoinContent = """
|
const bitcoinContent = """
|
||||||
|
|
||||||
|
class ElectrumSubAddress {
|
||||||
|
ElectrumSubAddress({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.address,
|
||||||
|
required this.txCount,
|
||||||
|
required this.balance,
|
||||||
|
required this.isChange});
|
||||||
|
final int id;
|
||||||
|
final String name;
|
||||||
|
final String address;
|
||||||
|
final int txCount;
|
||||||
|
final int balance;
|
||||||
|
final bool isChange;
|
||||||
|
}
|
||||||
|
|
||||||
abstract class Bitcoin {
|
abstract class Bitcoin {
|
||||||
TransactionPriority getMediumTransactionPriority();
|
TransactionPriority getMediumTransactionPriority();
|
||||||
|
|
||||||
|
@ -92,13 +111,16 @@ abstract class Bitcoin {
|
||||||
TransactionPriority deserializeBitcoinTransactionPriority(int raw);
|
TransactionPriority deserializeBitcoinTransactionPriority(int raw);
|
||||||
TransactionPriority deserializeLitecoinTransactionPriority(int raw);
|
TransactionPriority deserializeLitecoinTransactionPriority(int raw);
|
||||||
int getFeeRate(Object wallet, TransactionPriority priority);
|
int getFeeRate(Object wallet, TransactionPriority priority);
|
||||||
Future<void> generateNewAddress(Object wallet);
|
Future<void> generateNewAddress(Object wallet, String label);
|
||||||
|
Future<void> updateAddress(Object wallet,String address, String label);
|
||||||
Object createBitcoinTransactionCredentials(List<Output> outputs, {required TransactionPriority priority, int? feeRate});
|
Object createBitcoinTransactionCredentials(List<Output> outputs, {required TransactionPriority priority, int? feeRate});
|
||||||
Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs, {TransactionPriority? priority, required int feeRate});
|
Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs, {TransactionPriority? priority, required int feeRate});
|
||||||
|
|
||||||
List<String> getAddresses(Object wallet);
|
List<String> getAddresses(Object wallet);
|
||||||
String getAddress(Object wallet);
|
String getAddress(Object wallet);
|
||||||
|
|
||||||
|
List<ElectrumSubAddress> getSubAddresses(Object wallet);
|
||||||
|
|
||||||
String formatterBitcoinAmountToString({required int amount});
|
String formatterBitcoinAmountToString({required int amount});
|
||||||
double formatterBitcoinAmountToDouble({required int amount});
|
double formatterBitcoinAmountToDouble({required int amount});
|
||||||
int formatterStringDoubleToBitcoinAmount(String amount);
|
int formatterStringDoubleToBitcoinAmount(String amount);
|
||||||
|
|
Loading…
Reference in a new issue