mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-11-16 17:27:37 +00:00
Cw 744 improve address book (#1771)
* add sort function to contact list * fix UI * prevent duplicate contact names * dispose contact source subscription * fix custom order issue * update the address book UI * fix saving custom order * fix merge conflict issue * fix the address book filter by the selected currency * add dropdown for wallets with multiple address types * minor fixes * add dropdown for wallets with multiple address types * Update lib/entities/contact.dart [skip ci] * Update lib/src/screens/contact/contact_list_page.dart [skip ci] * Update lib/src/screens/contact/contact_list_page.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
2c37e427e9
commit
96db38c0aa
2 changed files with 75 additions and 22 deletions
|
@ -11,6 +11,7 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
|||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
|
@ -160,25 +161,60 @@ class _ContactPageBodyState extends State<ContactPageBody> with SingleTickerProv
|
|||
Widget _buildWalletContacts(BuildContext context) {
|
||||
final walletContacts = widget.contactListViewModel.walletContactsToShow;
|
||||
|
||||
final groupedContacts = <String, List<ContactBase>>{};
|
||||
for (var contact in walletContacts) {
|
||||
final baseName = _extractBaseName(contact.name);
|
||||
groupedContacts.putIfAbsent(baseName, () => []).add(contact);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: walletContacts.length * 2,
|
||||
itemCount: groupedContacts.length * 2,
|
||||
itemBuilder: (context, index) {
|
||||
if (index.isOdd) {
|
||||
return StandardListSeparator();
|
||||
} else {
|
||||
final walletInfo = walletContacts[index ~/ 2];
|
||||
return generateRaw(context, walletInfo);
|
||||
final groupIndex = index ~/ 2;
|
||||
final groupName = groupedContacts.keys.elementAt(groupIndex);
|
||||
final groupContacts = groupedContacts[groupName]!;
|
||||
|
||||
if (groupContacts.length == 1) {
|
||||
final contact = groupContacts[0];
|
||||
return generateRaw(context, contact);
|
||||
} else {
|
||||
final activeContact = groupContacts.firstWhere(
|
||||
(contact) => contact.name.contains('Active'),
|
||||
orElse: () => groupContacts[0],
|
||||
);
|
||||
|
||||
return ExpansionTile(
|
||||
title: Text(
|
||||
groupName,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
leading: _buildCurrencyIcon(activeContact),
|
||||
tilePadding: EdgeInsets.zero,
|
||||
childrenPadding: const EdgeInsets.only(left: 16),
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
expandedAlignment: Alignment.topLeft,
|
||||
children: groupContacts.map((contact) => generateRaw(context, contact)).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String _extractBaseName(String name) {
|
||||
final bracketIndex = name.indexOf('(');
|
||||
return (bracketIndex != -1) ? name.substring(0, bracketIndex).trim() : name;
|
||||
}
|
||||
|
||||
Widget generateRaw(BuildContext context, ContactBase contact) {
|
||||
final image = contact.type.iconPath;
|
||||
final currencyIcon = image != null
|
||||
? Image.asset(image, height: 24, width: 24)
|
||||
: const SizedBox(height: 24, width: 24);
|
||||
final currencyIcon = _buildCurrencyIcon(contact);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
|
@ -219,6 +255,13 @@ class _ContactPageBodyState extends State<ContactPageBody> with SingleTickerProv
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildCurrencyIcon(ContactBase contact) {
|
||||
final image = contact.type.iconPath;
|
||||
return image != null
|
||||
? Image.asset(image, height: 24, width: 24)
|
||||
: const SizedBox(height: 24, width: 24);
|
||||
}
|
||||
|
||||
Future<bool> showNameAndAddressDialog(BuildContext context, String name, String address) async {
|
||||
return await showPopUp<bool>(
|
||||
context: context,
|
||||
|
@ -263,12 +306,13 @@ class _ContactListBodyState extends State<ContactListBody> {
|
|||
@override
|
||||
void dispose() {
|
||||
widget.tabController.removeListener(_handleTabChange);
|
||||
widget.contactListViewModel.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final contacts = widget.contactListViewModel.contacts;
|
||||
final contacts = widget.contactListViewModel.contactsToShow;
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
child: FilteredList(
|
||||
|
|
|
@ -28,7 +28,8 @@ abstract class ContactListViewModelBase with Store {
|
|||
isAutoGenerateEnabled =
|
||||
settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.enabled {
|
||||
walletInfoSource.values.forEach((info) {
|
||||
if ([WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) && info.addressInfos != null) {
|
||||
if ([WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) &&
|
||||
info.addressInfos != null) {
|
||||
for (var key in info.addressInfos!.keys) {
|
||||
final value = info.addressInfos![key];
|
||||
final address = value?.first;
|
||||
|
@ -60,15 +61,19 @@ abstract class ContactListViewModelBase with Store {
|
|||
address,
|
||||
name,
|
||||
walletTypeToCryptoCurrency(info.type,
|
||||
isTestnet:
|
||||
info.network == null ? false : info.network!.toLowerCase().contains("testnet")),
|
||||
isTestnet: info.network == null
|
||||
? false
|
||||
: info.network!.toLowerCase().contains("testnet")),
|
||||
));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
walletContacts.add(WalletContact(
|
||||
info.address,
|
||||
_createName(info.name, "", key: [WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) ? 0 : null),
|
||||
_createName(info.name, "",
|
||||
key: [WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type)
|
||||
? 0
|
||||
: null),
|
||||
walletTypeToCryptoCurrency(info.type),
|
||||
));
|
||||
}
|
||||
|
@ -82,8 +87,11 @@ abstract class ContactListViewModelBase with Store {
|
|||
}
|
||||
|
||||
String _createName(String walletName, String label, {int? key = null}) {
|
||||
final actualLabel = label.replaceAll(RegExp(r'active', caseSensitive: false), S.current.active).replaceAll(RegExp(r'silent payments', caseSensitive: false), S.current.silent_payments);
|
||||
return '$walletName${key == null ? "" : " [#${key}]"} ${actualLabel.isNotEmpty ? "($actualLabel)" : ""}'.trim();
|
||||
final actualLabel = label
|
||||
.replaceAll(RegExp(r'active', caseSensitive: false), S.current.active)
|
||||
.replaceAll(RegExp(r'silent payments', caseSensitive: false), S.current.silent_payments);
|
||||
return '$walletName${key == null ? "" : " [#${key}]"} ${actualLabel.isNotEmpty ? "($actualLabel)" : ""}'
|
||||
.trim();
|
||||
}
|
||||
|
||||
final bool isAutoGenerateEnabled;
|
||||
|
@ -108,18 +116,19 @@ abstract class ContactListViewModelBase with Store {
|
|||
Future<void> delete(ContactRecord contact) async => contact.original.delete();
|
||||
|
||||
ObservableList<ContactRecord> get contactsToShow =>
|
||||
ObservableList.of(contacts.where((element) => _isValidForCurrency(element)));
|
||||
ObservableList.of(contacts.where((element) => _isValidForCurrency(element, false)));
|
||||
|
||||
@computed
|
||||
List<WalletContact> get walletContactsToShow =>
|
||||
walletContacts.where((element) => _isValidForCurrency(element)).toList();
|
||||
walletContacts.where((element) => _isValidForCurrency(element, true)).toList();
|
||||
|
||||
bool _isValidForCurrency(ContactBase element) {
|
||||
if (element.name.contains('Silent Payments')) return false;
|
||||
if (element.name.contains('MWEB')) return false;
|
||||
bool _isValidForCurrency(ContactBase element, bool isWalletContact) {
|
||||
if (_currency == null) return true;
|
||||
if (!element.name.contains('Active') &&
|
||||
isWalletContact &&
|
||||
(element.type == CryptoCurrency.btc || element.type == CryptoCurrency.ltc)) return false;
|
||||
|
||||
return _currency == null ||
|
||||
element.type == _currency ||
|
||||
return element.type == _currency ||
|
||||
(element.type.tag != null &&
|
||||
_currency?.tag != null &&
|
||||
element.type.tag == _currency?.tag) ||
|
||||
|
|
Loading…
Reference in a new issue