mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
CW-611-Refactor-Address-Handling (#1630)
* subaddress fix * fix subaddress generation * rewrite usedAddresses for xmr and wow * [skip ci] remove print statements * refactor address handling * do not remove manual addresses, just mark them * monero display latest address on receive page when autogenerate is enabled [skip ci] * WIP subaddresses, hidden addresses, and UI improvements for monero * update configure script * fix subaddress generation, display latest address * Update lib/core/wallet_loading_service.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Exclude manually created addresses Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * don't call .save function multiple times Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * - revert usedAddress functinality - add mutex to prevent crashes - fix UI flashing in tx screen - fixes from comments * account index fixes added code to wownero code comment * - added subaddress index - fixed received count also accounting for change (we don't want that) - fix bad state: no element - fix search - fix automatic generation * prevent crashes by acquiring mutex before setting the pointer * - fix ttDetails generation in larger/restored wallets - show manual add icon in monero/wownero even when autogeneration is enabled - disable colors on non-debug builds - cache getAddress call in xmr/wow [skip ci] * fix: silent payment error in address setter enable fancy new features only for xmr / wow * refresh subaddress list, when we add new address fix manual addresses marking * add toggle to hide and show address * update transaction details after restore * show only one address in address book for xmr, wow and haven * fix address book reset address only when autogenerate is on * enable isEnabledAutoGenerateSubaddress on new wallets * hide addresses after exchange only for XMR and WOW * fix: bad-state no element * Update cw_monero/lib/monero_wallet_addresses.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Update cw_monero/lib/monero_wallet_addresses.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * improvements to performance * 0, 0 -> accountIndex, addressIndex * make constant variables final * Update cw_wownero/lib/wownero_wallet_addresses.dart [skip ci] * Update cw_wownero/lib/wownero_wallet_addresses.dart [skip ci] * Update cw_monero/lib/monero_wallet.dart [skip ci] * fix potential exception * fix after removing late * remove orElse, replaced it with a try catch block. fix strings * fix valid seed function * fix null check error [skip ci] * fix updateSubaddressList for wow and haven --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
@@ -61,7 +61,13 @@ class CWMoneroSubaddressList extends MoneroSubaddressList {
|
||||
ObservableList<Subaddress> get subaddresses {
|
||||
final moneroWallet = _wallet as MoneroWallet;
|
||||
final subAddresses = moneroWallet.walletAddresses.subaddressList.subaddresses
|
||||
.map((sub) => Subaddress(id: sub.id, address: sub.address, label: sub.label))
|
||||
.map((sub) => Subaddress(
|
||||
id: sub.id,
|
||||
address: sub.address,
|
||||
label: sub.label,
|
||||
received: sub.balance??"unknown",
|
||||
txCount: sub.txCount??0,
|
||||
))
|
||||
.toList();
|
||||
return ObservableList<Subaddress>.of(subAddresses);
|
||||
}
|
||||
@@ -83,7 +89,12 @@ class CWMoneroSubaddressList extends MoneroSubaddressList {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.walletAddresses.subaddressList
|
||||
.getAll()
|
||||
.map((sub) => Subaddress(id: sub.id, label: sub.label, address: sub.address))
|
||||
.map((sub) => Subaddress(
|
||||
id: sub.id,
|
||||
label: sub.label,
|
||||
address: sub.address,
|
||||
txCount: sub.txCount??0,
|
||||
received: sub.balance??'unknown'))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -91,7 +102,7 @@ class CWMoneroSubaddressList extends MoneroSubaddressList {
|
||||
Future<void> addSubaddress(Object wallet,
|
||||
{required int accountIndex, required String label}) async {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
await moneroWallet.walletAddresses.subaddressList
|
||||
return await moneroWallet.walletAddresses.subaddressList
|
||||
.addSubaddress(accountIndex: accountIndex, label: label);
|
||||
}
|
||||
|
||||
|
||||
@@ -155,13 +155,14 @@ class AddressPage extends BasePage {
|
||||
amountTextFieldFocusNode: _cryptoAmountFocus,
|
||||
amountController: _amountController,
|
||||
isLight: dashboardViewModel.settingsStore.currentTheme.type ==
|
||||
ThemeType.light))),
|
||||
ThemeType.light,
|
||||
))),
|
||||
SizedBox(height: 16),
|
||||
Observer(builder: (_) {
|
||||
if (addressListViewModel.hasAddressList) {
|
||||
return SelectButton(
|
||||
text: addressListViewModel.buttonTitle,
|
||||
onTap: () async => Navigator.of(context).pushNamed(Routes.receive),
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.receive),
|
||||
textColor: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||
borderColor: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
||||
|
||||
@@ -509,7 +509,7 @@ class ExchangePage extends BasePage {
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => exchangeViewModel.wallet.walletAddresses.address, (String address) {
|
||||
reaction((_) => exchangeViewModel.wallet.walletAddresses.addressForExchange, (String address) {
|
||||
if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
|
||||
depositKey.currentState!.changeAddress(address: address);
|
||||
}
|
||||
@@ -565,7 +565,7 @@ class ExchangePage extends BasePage {
|
||||
key.currentState!.changeWalletName(isCurrentTypeWallet ? exchangeViewModel.wallet.name : '');
|
||||
|
||||
key.currentState!.changeAddress(
|
||||
address: isCurrentTypeWallet ? exchangeViewModel.wallet.walletAddresses.address : '');
|
||||
address: isCurrentTypeWallet ? exchangeViewModel.wallet.walletAddresses.addressForExchange : '');
|
||||
|
||||
key.currentState!.changeAmount(amount: '');
|
||||
}
|
||||
@@ -576,9 +576,9 @@ class ExchangePage extends BasePage {
|
||||
|
||||
if (isCurrentTypeWallet) {
|
||||
key.currentState!.changeWalletName(exchangeViewModel.wallet.name);
|
||||
key.currentState!.addressController.text = exchangeViewModel.wallet.walletAddresses.address;
|
||||
key.currentState!.addressController.text = exchangeViewModel.wallet.walletAddresses.addressForExchange;
|
||||
} else if (key.currentState!.addressController.text ==
|
||||
exchangeViewModel.wallet.walletAddresses.address) {
|
||||
exchangeViewModel.wallet.walletAddresses.addressForExchange) {
|
||||
key.currentState!.changeWalletName('');
|
||||
key.currentState!.addressController.text = '';
|
||||
}
|
||||
@@ -629,7 +629,7 @@ class ExchangePage extends BasePage {
|
||||
initialCurrency: exchangeViewModel.depositCurrency,
|
||||
initialWalletName: depositWalletName ?? '',
|
||||
initialAddress: exchangeViewModel.depositCurrency == exchangeViewModel.wallet.currency
|
||||
? exchangeViewModel.wallet.walletAddresses.address
|
||||
? exchangeViewModel.wallet.walletAddresses.addressForExchange
|
||||
: exchangeViewModel.depositAddress,
|
||||
initialIsAmountEditable: true,
|
||||
initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
|
||||
@@ -694,7 +694,7 @@ class ExchangePage extends BasePage {
|
||||
initialCurrency: exchangeViewModel.receiveCurrency,
|
||||
initialWalletName: receiveWalletName ?? '',
|
||||
initialAddress: exchangeViewModel.receiveCurrency == exchangeViewModel.wallet.currency
|
||||
? exchangeViewModel.wallet.walletAddresses.address
|
||||
? exchangeViewModel.wallet.walletAddresses.addressForExchange
|
||||
: exchangeViewModel.receiveAddress,
|
||||
initialIsAmountEditable: exchangeViewModel.isReceiveAmountEditable,
|
||||
isAmountEstimated: true,
|
||||
|
||||
@@ -129,7 +129,7 @@ class ExchangeTemplatePage extends BasePage {
|
||||
initialWalletName: depositWalletName ?? '',
|
||||
initialAddress: exchangeViewModel.depositCurrency ==
|
||||
exchangeViewModel.wallet.currency
|
||||
? exchangeViewModel.wallet.walletAddresses.address
|
||||
? exchangeViewModel.wallet.walletAddresses.addressForExchange
|
||||
: exchangeViewModel.depositAddress,
|
||||
initialIsAmountEditable: true,
|
||||
initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
|
||||
@@ -166,7 +166,7 @@ class ExchangeTemplatePage extends BasePage {
|
||||
initialWalletName: receiveWalletName ?? '',
|
||||
initialAddress: exchangeViewModel.receiveCurrency ==
|
||||
exchangeViewModel.wallet.currency
|
||||
? exchangeViewModel.wallet.walletAddresses.address
|
||||
? exchangeViewModel.wallet.walletAddresses.addressForExchange
|
||||
: exchangeViewModel.receiveAddress,
|
||||
initialIsAmountEditable: false,
|
||||
isAmountEstimated: true,
|
||||
|
||||
@@ -121,7 +121,8 @@ class ReceivePage extends BasePage {
|
||||
heroTag: _heroTag,
|
||||
amountTextFieldFocusNode: _cryptoAmountFocus,
|
||||
amountController: _amountController,
|
||||
isLight: currentTheme.type == ThemeType.light),
|
||||
isLight: currentTheme.type == ThemeType.light,
|
||||
),
|
||||
),
|
||||
AddressList(addressListViewModel: addressListViewModel),
|
||||
Padding(
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
|
||||
@@ -15,11 +16,14 @@ class AddressCell extends StatelessWidget {
|
||||
required this.textColor,
|
||||
this.onTap,
|
||||
this.onEdit,
|
||||
this.onHide,
|
||||
this.isHidden = false,
|
||||
this.onDelete,
|
||||
this.txCount,
|
||||
this.balance,
|
||||
this.isChange = false,
|
||||
this.hasBalance = false});
|
||||
this.hasBalance = false,
|
||||
this.hasReceived = false});
|
||||
|
||||
factory AddressCell.fromItem(
|
||||
WalletAddressListItem item, {
|
||||
@@ -28,7 +32,10 @@ class AddressCell extends StatelessWidget {
|
||||
required Color textColor,
|
||||
Function(String)? onTap,
|
||||
bool hasBalance = false,
|
||||
bool hasReceived = false,
|
||||
Function()? onEdit,
|
||||
Function()? onHide,
|
||||
bool isHidden = false,
|
||||
Function()? onDelete,
|
||||
}) =>
|
||||
AddressCell(
|
||||
@@ -40,11 +47,14 @@ class AddressCell extends StatelessWidget {
|
||||
textColor: textColor,
|
||||
onTap: onTap,
|
||||
onEdit: onEdit,
|
||||
onHide: onHide,
|
||||
isHidden: isHidden,
|
||||
onDelete: onDelete,
|
||||
txCount: item.txCount,
|
||||
balance: item.balance,
|
||||
isChange: item.isChange,
|
||||
hasBalance: hasBalance);
|
||||
hasBalance: hasBalance,
|
||||
hasReceived: hasReceived,);
|
||||
|
||||
final String address;
|
||||
final String name;
|
||||
@@ -54,11 +64,14 @@ class AddressCell extends StatelessWidget {
|
||||
final Color textColor;
|
||||
final Function(String)? onTap;
|
||||
final Function()? onEdit;
|
||||
final Function()? onHide;
|
||||
final bool isHidden;
|
||||
final Function()? onDelete;
|
||||
final int? txCount;
|
||||
final String? balance;
|
||||
final bool isChange;
|
||||
final bool hasBalance;
|
||||
final bool hasReceived;
|
||||
|
||||
static const int addressPreviewLength = 8;
|
||||
|
||||
@@ -138,7 +151,7 @@ class AddressCell extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (hasBalance)
|
||||
if (hasBalance || hasReceived)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Row(
|
||||
@@ -146,7 +159,7 @@ class AddressCell extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text(
|
||||
'${S.of(context).balance}: $balance',
|
||||
'${hasReceived ? S.of(context).received : S.of(context).balance}: $balance',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -178,14 +191,28 @@ class AddressCell extends StatelessWidget {
|
||||
enabled: !isCurrent,
|
||||
child: Slidable(
|
||||
key: Key(address),
|
||||
startActionPane: _actionPane(context),
|
||||
endActionPane: _actionPane(context),
|
||||
startActionPane: _actionPaneStart(context),
|
||||
endActionPane: _actionPaneEnd(context),
|
||||
child: cell,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ActionPane _actionPane(BuildContext context) => ActionPane(
|
||||
ActionPane _actionPaneEnd(BuildContext context) => ActionPane(
|
||||
motion: const ScrollMotion(),
|
||||
extentRatio: onDelete != null ? 0.4 : 0.3,
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (_) => onHide?.call(),
|
||||
backgroundColor: isHidden ? Colors.green : Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
icon: isHidden ? CupertinoIcons.arrow_left : CupertinoIcons.arrow_right,
|
||||
label: isHidden ? S.of(context).show : S.of(context).hide,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
ActionPane _actionPaneStart(BuildContext context) => ActionPane(
|
||||
motion: const ScrollMotion(),
|
||||
extentRatio: onDelete != null ? 0.4 : 0.3,
|
||||
children: [
|
||||
|
||||
@@ -10,16 +10,19 @@ import 'package:cake_wallet/src/screens/receive/widgets/address_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart';
|
||||
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||
import 'package:cake_wallet/utils/list_item.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.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_hidden_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_view_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class AddressList extends StatelessWidget {
|
||||
class AddressList extends StatefulWidget {
|
||||
const AddressList({
|
||||
super.key,
|
||||
required this.addressListViewModel,
|
||||
@@ -29,59 +32,123 @@ class AddressList extends StatelessWidget {
|
||||
final WalletAddressListViewModel addressListViewModel;
|
||||
final Function(String)? onSelect;
|
||||
|
||||
@override
|
||||
State<AddressList> createState() => _AddressListState();
|
||||
}
|
||||
|
||||
class _AddressListState extends State<AddressList> {
|
||||
|
||||
bool showHiddenAddresses = false;
|
||||
|
||||
void _toggleHiddenAddresses() {
|
||||
setState(() {
|
||||
showHiddenAddresses = !showHiddenAddresses;
|
||||
});
|
||||
updateItems();
|
||||
}
|
||||
|
||||
List<ListItem> getItems(List<ListItem> list, bool showHidden) {
|
||||
return list.where((element) {
|
||||
if (element is WalletAddressListItem) {
|
||||
if (showHidden && element.isHidden) return true;
|
||||
if (!showHidden && !element.isHidden) return true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<ListItem> items = [];
|
||||
|
||||
void updateItems() {
|
||||
setState(() {
|
||||
items = getItems(widget.addressListViewModel.items, showHiddenAddresses);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
items = getItems(widget.addressListViewModel.items, showHiddenAddresses);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool editable = onSelect == null;
|
||||
return Observer(
|
||||
builder: (_) => ListView.separated(
|
||||
padding: EdgeInsets.all(0),
|
||||
separatorBuilder: (context, _) => const HorizontalSectionDivider(),
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: addressListViewModel.items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = addressListViewModel.items[index];
|
||||
Widget cell = Container();
|
||||
bool editable = widget.onSelect == null;
|
||||
return ListView.separated(
|
||||
padding: EdgeInsets.all(0),
|
||||
separatorBuilder: (context, _) => const HorizontalSectionDivider(),
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = items[index];
|
||||
Widget cell = Container();
|
||||
|
||||
if (item is WalletAccountListHeader) {
|
||||
cell = HeaderTile(
|
||||
showTrailingButton: true,
|
||||
walletAddressListViewModel: addressListViewModel,
|
||||
trailingButtonTap: () async {
|
||||
if (addressListViewModel.type == WalletType.monero ||
|
||||
addressListViewModel.type == WalletType.haven) {
|
||||
await showPopUp<void>(
|
||||
context: context, builder: (_) => getIt.get<MoneroAccountListPage>());
|
||||
} else {
|
||||
await showPopUp<void>(
|
||||
context: context, builder: (_) => getIt.get<NanoAccountListPage>());
|
||||
}
|
||||
},
|
||||
title: S.of(context).accounts,
|
||||
trailingIcon: Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
));
|
||||
}
|
||||
if (item is WalletAccountListHeader) {
|
||||
cell = HeaderTile(
|
||||
showTrailingButton: true,
|
||||
walletAddressListViewModel: widget.addressListViewModel,
|
||||
trailingButtonTap: () async {
|
||||
if (widget.addressListViewModel.type == WalletType.monero ||
|
||||
widget.addressListViewModel.type == WalletType.haven) {
|
||||
await showPopUp<void>(
|
||||
context: context, builder: (_) => getIt.get<MoneroAccountListPage>());
|
||||
updateItems();
|
||||
} else {
|
||||
await showPopUp<void>(
|
||||
context: context, builder: (_) => getIt.get<NanoAccountListPage>());
|
||||
updateItems();
|
||||
}
|
||||
},
|
||||
title: S.of(context).accounts,
|
||||
trailingIcon: Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
));
|
||||
}
|
||||
|
||||
if (item is WalletAddressListHeader) {
|
||||
cell = HeaderTile(
|
||||
title: S.of(context).addresses,
|
||||
walletAddressListViewModel: addressListViewModel,
|
||||
showTrailingButton: !addressListViewModel.isAutoGenerateSubaddressEnabled,
|
||||
showSearchButton: true,
|
||||
trailingButtonTap: () => Navigator.of(context).pushNamed(Routes.newSubaddress),
|
||||
trailingIcon: Icon(
|
||||
Icons.add,
|
||||
size: 20,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
));
|
||||
}
|
||||
if (item is WalletAddressHiddenListHeader) {
|
||||
cell = HeaderTile(
|
||||
title: S.of(context).hidden_addresses,
|
||||
walletAddressListViewModel: widget.addressListViewModel,
|
||||
showTrailingButton: true,
|
||||
showSearchButton: false,
|
||||
trailingButtonTap: _toggleHiddenAddresses,
|
||||
trailingIcon: Icon(
|
||||
showHiddenAddresses ? Icons.toggle_on : Icons.toggle_off,
|
||||
size: 20,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
));
|
||||
}
|
||||
|
||||
if (item is WalletAddressListItem) {
|
||||
if (item is WalletAddressListHeader) {
|
||||
cell = HeaderTile(
|
||||
title: S.of(context).addresses,
|
||||
walletAddressListViewModel: widget.addressListViewModel,
|
||||
showTrailingButton: widget.addressListViewModel.showAddManualAddresses,
|
||||
showSearchButton: true,
|
||||
onSearchCallback: updateItems,
|
||||
trailingButtonTap: () => Navigator.of(context).pushNamed(Routes.newSubaddress).then((value) {
|
||||
updateItems(); // refresh the new address
|
||||
}),
|
||||
trailingIcon: Icon(
|
||||
Icons.add,
|
||||
size: 20,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
));
|
||||
}
|
||||
|
||||
if (item is WalletAddressListItem) {
|
||||
if (item.isHidden && !showHiddenAddresses) {
|
||||
cell = Container();
|
||||
} else if (!item.isHidden && showHiddenAddresses) {
|
||||
cell = Container();
|
||||
} else {
|
||||
cell = Observer(builder: (_) {
|
||||
final isCurrent = item.address == addressListViewModel.address.address && editable;
|
||||
final isCurrent = item.address == widget.addressListViewModel.address.address && editable;
|
||||
final backgroundColor = isCurrent
|
||||
? Theme.of(context).extension<ReceivePageTheme>()!.currentTileBackgroundColor
|
||||
: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor;
|
||||
@@ -89,35 +156,51 @@ class AddressList extends StatelessWidget {
|
||||
? Theme.of(context).extension<ReceivePageTheme>()!.currentTileTextColor
|
||||
: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor;
|
||||
|
||||
|
||||
return AddressCell.fromItem(
|
||||
item,
|
||||
isCurrent: isCurrent,
|
||||
hasBalance: addressListViewModel.isElectrumWallet,
|
||||
backgroundColor: backgroundColor,
|
||||
hasBalance: widget.addressListViewModel.isBalanceAvailable,
|
||||
hasReceived: widget.addressListViewModel.isReceivedAvailable,
|
||||
// hasReceived:
|
||||
backgroundColor: (kDebugMode && item.isHidden) ?
|
||||
Theme.of(context).colorScheme.error :
|
||||
(kDebugMode && item.isManual) ? Theme.of(context).colorScheme.error.withBlue(255) :
|
||||
backgroundColor,
|
||||
textColor: textColor,
|
||||
onTap: (_) {
|
||||
if (onSelect != null) {
|
||||
onSelect!(item.address);
|
||||
if (widget.onSelect != null) {
|
||||
widget.onSelect!(item.address);
|
||||
return;
|
||||
}
|
||||
addressListViewModel.setAddress(item);
|
||||
widget.addressListViewModel.setAddress(item);
|
||||
},
|
||||
onEdit: editable
|
||||
? () => Navigator.of(context).pushNamed(Routes.newSubaddress, arguments: item)
|
||||
? () => Navigator.of(context).pushNamed(Routes.newSubaddress, arguments: item).then((value) {
|
||||
updateItems(); // refresh the new address
|
||||
})
|
||||
: null,
|
||||
isHidden: item.isHidden,
|
||||
onHide: () => _hideAddress(item),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return index != 0
|
||||
? cell
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(30), topRight: Radius.circular(30)),
|
||||
child: cell,
|
||||
);
|
||||
},
|
||||
),
|
||||
return index != 0
|
||||
? cell
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(30), topRight: Radius.circular(30)),
|
||||
child: cell,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _hideAddress(WalletAddressListItem item) async {
|
||||
await widget.addressListViewModel.toggleHideAddress(item);
|
||||
updateItems();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ class HeaderTile extends StatefulWidget {
|
||||
this.showSearchButton = false,
|
||||
this.showTrailingButton = false,
|
||||
this.trailingButtonTap,
|
||||
this.onSearchCallback,
|
||||
this.trailingIcon,
|
||||
});
|
||||
|
||||
@@ -18,6 +19,7 @@ class HeaderTile extends StatefulWidget {
|
||||
final bool showSearchButton;
|
||||
final bool showTrailingButton;
|
||||
final VoidCallback? trailingButtonTap;
|
||||
final VoidCallback? onSearchCallback;
|
||||
final Icon? trailingIcon;
|
||||
|
||||
@override
|
||||
@@ -41,7 +43,10 @@ class _HeaderTileState extends State<HeaderTile> {
|
||||
_isSearchActive
|
||||
? Expanded(
|
||||
child: TextField(
|
||||
onChanged: (value) => widget.walletAddressListViewModel.updateSearchText(value),
|
||||
onChanged: (value) {
|
||||
widget.walletAddressListViewModel.updateSearchText(value);
|
||||
widget.onSearchCallback?.call();
|
||||
},
|
||||
cursorColor: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
|
||||
cursorWidth: 0.5,
|
||||
decoration: InputDecoration(
|
||||
|
||||
@@ -37,6 +37,10 @@ class QRWidget extends StatelessWidget {
|
||||
final int? qrVersion;
|
||||
final String? heroTag;
|
||||
|
||||
PaymentURI get addressUri {
|
||||
return addressListViewModel.uri;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final copyImage = Image.asset('assets/images/copy_address.png',
|
||||
@@ -77,14 +81,14 @@ class QRWidget extends StatelessWidget {
|
||||
() async {
|
||||
await Navigator.pushNamed(context, Routes.fullscreenQR,
|
||||
arguments: QrViewData(
|
||||
data: addressListViewModel.uri.toString(),
|
||||
data: addressUri.toString(),
|
||||
heroTag: heroTag,
|
||||
));
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Hero(
|
||||
tag: Key(heroTag ?? addressListViewModel.uri.toString()),
|
||||
tag: Key(heroTag ?? addressUri.toString()),
|
||||
child: Center(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
@@ -105,7 +109,7 @@ class QRWidget extends StatelessWidget {
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
child: QrImage(data: addressListViewModel.uri.toString())),
|
||||
child: QrImage(data: addressUri.toString())),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -148,7 +152,7 @@ class QRWidget extends StatelessWidget {
|
||||
builder: (context) => Observer(
|
||||
builder: (context) => GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: addressListViewModel.address.address));
|
||||
Clipboard.setData(ClipboardData(text: addressUri.address));
|
||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||
},
|
||||
child: Row(
|
||||
@@ -157,7 +161,7 @@ class QRWidget extends StatelessWidget {
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
addressListViewModel.address.address,
|
||||
addressUri.address,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
|
||||
@@ -27,32 +27,41 @@ abstract class ContactListViewModelBase with Store {
|
||||
settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.enabled {
|
||||
walletInfoSource.values.forEach((info) {
|
||||
if (isAutoGenerateEnabled && info.type == WalletType.monero && info.addressInfos != null) {
|
||||
info.addressInfos!.forEach((key, value) {
|
||||
final nextUnusedAddress = value.firstWhereOrNull(
|
||||
(addressInfo) => !(info.usedAddresses?.contains(addressInfo.address) ?? false));
|
||||
if (nextUnusedAddress != null) {
|
||||
final name = _createName(info.name, nextUnusedAddress.label);
|
||||
walletContacts.add(WalletContact(
|
||||
nextUnusedAddress.address,
|
||||
name,
|
||||
walletTypeToCryptoCurrency(info.type),
|
||||
));
|
||||
}
|
||||
});
|
||||
final key = info.addressInfos!.keys.first;
|
||||
final value = info.addressInfos![key];
|
||||
final address = value?.first;
|
||||
if (address != null) {
|
||||
final name = _createName(info.name, address.label);
|
||||
walletContacts.add(WalletContact(
|
||||
address.address,
|
||||
name,
|
||||
walletTypeToCryptoCurrency(info.type),
|
||||
));
|
||||
}
|
||||
} else if (info.addresses?.isNotEmpty == true && info.addresses!.length > 1) {
|
||||
info.addresses!.forEach((address, label) {
|
||||
if (label.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final name = _createName(info.name, label);
|
||||
if ([WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type)) {
|
||||
final address = info.address;
|
||||
final name = _createName(info.name, "");
|
||||
walletContacts.add(WalletContact(
|
||||
address,
|
||||
name,
|
||||
walletTypeToCryptoCurrency(info.type,
|
||||
isTestnet:
|
||||
info.network == null ? false : info.network!.toLowerCase().contains("testnet")),
|
||||
walletTypeToCryptoCurrency(info.type),
|
||||
));
|
||||
});
|
||||
} else {
|
||||
info.addresses!.forEach((address, label) {
|
||||
if (label.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final name = _createName(info.name, label);
|
||||
walletContacts.add(WalletContact(
|
||||
address,
|
||||
name,
|
||||
walletTypeToCryptoCurrency(info.type,
|
||||
isTestnet:
|
||||
info.network == null ? false : info.network!.toLowerCase().contains("testnet")),
|
||||
));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
walletContacts.add(WalletContact(
|
||||
info.address,
|
||||
|
||||
@@ -121,7 +121,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
depositAmount = '';
|
||||
receiveAmount = '';
|
||||
receiveAddress = '';
|
||||
depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.address : '';
|
||||
depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.addressForExchange : '';
|
||||
provider = providersForCurrentPair().first;
|
||||
final initialProvider = provider;
|
||||
provider!.checkIsAvailable().then((bool isAvailable) {
|
||||
@@ -155,6 +155,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
wallet.type == WalletType.litecoin ||
|
||||
wallet.type == WalletType.bitcoinCash;
|
||||
|
||||
bool get hideAddressAfterExchange =>
|
||||
wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.wownero;
|
||||
|
||||
bool _useTorOnly;
|
||||
final Box<Trade> trades;
|
||||
final ExchangeTemplateStore _exchangeTemplateStore;
|
||||
@@ -540,6 +544,11 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
isFixedRate: isFixedRateMode,
|
||||
);
|
||||
|
||||
if (hideAddressAfterExchange) {
|
||||
wallet.walletAddresses.hiddenAddresses.add(depositAddress);
|
||||
await wallet.walletAddresses.saveAddressesInBox();
|
||||
}
|
||||
|
||||
var amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
amount = amount.replaceAll(',', '.');
|
||||
|
||||
@@ -603,8 +612,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
isReceiveAmountEntered = false;
|
||||
depositAmount = '';
|
||||
receiveAmount = '';
|
||||
depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.address : '';
|
||||
receiveAddress = receiveCurrency == wallet.currency ? wallet.walletAddresses.address : '';
|
||||
depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.addressForExchange : '';
|
||||
receiveAddress = receiveCurrency == wallet.currency ? wallet.walletAddresses.addressForExchange : '';
|
||||
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
|
||||
isFixedRateMode = false;
|
||||
_onPairChange();
|
||||
|
||||
@@ -78,6 +78,8 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||
wallet,
|
||||
accountIndex: monero!.getCurrentAccount(wallet).id,
|
||||
label: label);
|
||||
final addr = await monero!.getSubaddressList(wallet).subaddresses.first.address; // first because the order is reversed
|
||||
wallet.walletAddresses.manualAddresses.add(addr);
|
||||
await wallet.save();
|
||||
}
|
||||
|
||||
@@ -88,6 +90,8 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||
wallet,
|
||||
accountIndex: wownero!.getCurrentAccount(wallet).id,
|
||||
label: label);
|
||||
final addr = await wownero!.getSubaddressList(wallet).subaddresses.first.address; // first because the order is reversed
|
||||
wallet.walletAddresses.manualAddresses.add(addr);
|
||||
await wallet.save();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import 'package:cake_wallet/utils/list_item.dart';
|
||||
|
||||
class WalletAddressHiddenListHeader extends ListItem {}
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:cake_wallet/utils/list_item.dart';
|
||||
|
||||
class WalletAddressListItem extends ListItem {
|
||||
const WalletAddressListItem({
|
||||
WalletAddressListItem({
|
||||
required this.address,
|
||||
required this.isPrimary,
|
||||
this.id,
|
||||
@@ -11,6 +11,8 @@ class WalletAddressListItem extends ListItem {
|
||||
this.isChange = false,
|
||||
// Address that is only ever used once, shouldn't be used to receive funds, copy and paste, share etc
|
||||
this.isOneTimeReceiveAddress = false,
|
||||
this.isHidden = false,
|
||||
this.isManual = false,
|
||||
}) : super();
|
||||
|
||||
final int? id;
|
||||
@@ -20,6 +22,8 @@ class WalletAddressListItem extends ListItem {
|
||||
final int? txCount;
|
||||
final String? balance;
|
||||
final bool isChange;
|
||||
bool isHidden;
|
||||
bool isManual;
|
||||
final bool? isOneTimeReceiveAddress;
|
||||
|
||||
@override
|
||||
|
||||
@@ -18,12 +18,16 @@ import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||
import 'package:cake_wallet/tron/tron.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_address_hidden_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/wownero/wownero.dart';
|
||||
import 'package:cw_core/amount_converter.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_monero/api/wallet.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
@@ -271,56 +275,40 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
WalletType get type => wallet.type;
|
||||
|
||||
@computed
|
||||
WalletAddressListItem get address =>
|
||||
WalletAddressListItem(address: wallet.walletAddresses.address, isPrimary: false);
|
||||
WalletAddressListItem get address {
|
||||
return WalletAddressListItem(address: wallet.walletAddresses.address, isPrimary: false);
|
||||
}
|
||||
|
||||
@computed
|
||||
PaymentURI get uri {
|
||||
if (wallet.type == WalletType.monero) {
|
||||
return MoneroURI(amount: amount, address: address.address);
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
return MoneroURI(amount: amount, address: address.address);
|
||||
case WalletType.haven:
|
||||
return HavenURI(amount: amount, address: address.address);
|
||||
case WalletType.bitcoin:
|
||||
return BitcoinURI(amount: amount, address: address.address);
|
||||
case WalletType.litecoin:
|
||||
return LitecoinURI(amount: amount, address: address.address);
|
||||
case WalletType.ethereum:
|
||||
return EthereumURI(amount: amount, address: address.address);
|
||||
case WalletType.bitcoinCash:
|
||||
return BitcoinCashURI(amount: amount, address: address.address);
|
||||
case WalletType.banano:
|
||||
return NanoURI(amount: amount, address: address.address);
|
||||
case WalletType.nano:
|
||||
return NanoURI(amount: amount, address: address.address);
|
||||
case WalletType.polygon:
|
||||
return PolygonURI(amount: amount, address: address.address);
|
||||
case WalletType.solana:
|
||||
return SolanaURI(amount: amount, address: address.address);
|
||||
case WalletType.tron:
|
||||
return TronURI(amount: amount, address: address.address);
|
||||
case WalletType.wownero:
|
||||
return WowneroURI(amount: amount, address: address.address);
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.haven) {
|
||||
return HavenURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.bitcoin) {
|
||||
return BitcoinURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.litecoin) {
|
||||
return LitecoinURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.ethereum) {
|
||||
return EthereumURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.bitcoinCash) {
|
||||
return BitcoinCashURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.nano) {
|
||||
return NanoURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.polygon) {
|
||||
return PolygonURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.solana) {
|
||||
return SolanaURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.tron) {
|
||||
return TronURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.wownero) {
|
||||
return WowneroURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
|
||||
@computed
|
||||
@@ -341,7 +329,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
id: subaddress.id,
|
||||
isPrimary: isPrimary,
|
||||
name: subaddress.label,
|
||||
address: subaddress.address);
|
||||
address: subaddress.address,
|
||||
balance: subaddress.received,
|
||||
txCount: subaddress.txCount,
|
||||
);
|
||||
});
|
||||
addressList.addAll(addressItems);
|
||||
}
|
||||
@@ -468,6 +459,16 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
|
||||
}
|
||||
|
||||
for (var i = 0; i < addressList.length; i++) {
|
||||
if (!(addressList[i] is WalletAddressListItem)) continue;
|
||||
(addressList[i] as WalletAddressListItem).isHidden = wallet.walletAddresses.hiddenAddresses.contains((addressList[i] as WalletAddressListItem).address);
|
||||
}
|
||||
|
||||
for (var i = 0; i < addressList.length; i++) {
|
||||
if (!(addressList[i] is WalletAddressListItem)) continue;
|
||||
(addressList[i] as WalletAddressListItem).isManual = wallet.walletAddresses.manualAddresses.contains((addressList[i] as WalletAddressListItem).address);
|
||||
}
|
||||
|
||||
if (searchText.isNotEmpty) {
|
||||
return ObservableList.of(addressList.where((item) {
|
||||
if (item is WalletAddressListItem) {
|
||||
@@ -479,7 +480,21 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
|
||||
return addressList;
|
||||
}
|
||||
|
||||
Future<void> toggleHideAddress(WalletAddressListItem item) async {
|
||||
if (item.isHidden) {
|
||||
wallet.walletAddresses.hiddenAddresses.removeWhere((element) => element == item.address);
|
||||
} else {
|
||||
wallet.walletAddresses.hiddenAddresses.add(item.address);
|
||||
}
|
||||
await wallet.walletAddresses.saveAddressesInBox();
|
||||
if (wallet.type == WalletType.monero) {
|
||||
monero!.getSubaddressList(wallet).update(wallet, accountIndex: monero!.getCurrentAccount(wallet).id);
|
||||
} else if (wallet.type == WalletType.wownero) {
|
||||
wownero!.getSubaddressList(wallet).update(wallet, accountIndex: wownero!.getCurrentAccount(wallet).id);
|
||||
} else if (wallet.type == WalletType.haven) {
|
||||
haven!.getSubaddressList(wallet).update(wallet, accountIndex: haven!.getCurrentAccount(wallet).id);
|
||||
}
|
||||
}
|
||||
@observable
|
||||
bool hasAccounts;
|
||||
|
||||
@@ -515,6 +530,14 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
wallet.type == WalletType.litecoin ||
|
||||
wallet.type == WalletType.bitcoinCash;
|
||||
|
||||
@computed
|
||||
bool get isBalanceAvailable => isElectrumWallet;
|
||||
|
||||
@computed
|
||||
bool get isReceivedAvailable =>
|
||||
wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.wownero;
|
||||
|
||||
@computed
|
||||
bool get isSilentPayments =>
|
||||
wallet.type == WalletType.bitcoin && bitcoin!.hasSelectedSilentPayments(wallet);
|
||||
@@ -524,6 +547,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
_settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled &&
|
||||
!isSilentPayments;
|
||||
|
||||
@computed
|
||||
bool get showAddManualAddresses =>
|
||||
!isAutoGenerateSubaddressEnabled ||
|
||||
wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.wownero;
|
||||
|
||||
List<ListItem> _baseItems;
|
||||
|
||||
final YatStore yatStore;
|
||||
@@ -542,6 +571,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
void _init() {
|
||||
_baseItems = [];
|
||||
|
||||
if (wallet.walletAddresses.hiddenAddresses.isNotEmpty) {
|
||||
_baseItems.add(WalletAddressHiddenListHeader());
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.wownero ||
|
||||
wallet.type == WalletType.haven) {
|
||||
@@ -551,6 +584,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
if (wallet.type != WalletType.nano && wallet.type != WalletType.banano) {
|
||||
_baseItems.add(WalletAddressListHeader());
|
||||
}
|
||||
if (wallet.isEnabledAutoGenerateSubaddress) {
|
||||
wallet.walletAddresses.address = wallet.walletAddresses.latestAddress;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
|
||||
Reference in New Issue
Block a user