refactor: contact pages and update routing

This commit is contained in:
Serhii 2025-06-23 16:31:26 +03:00
parent a298a28f56
commit f89c06e5df
10 changed files with 69 additions and 221 deletions

View file

@ -34,12 +34,11 @@ import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/haven/cw_haven.dart';
import 'package:cake_wallet/src/screens/address_book/edit_address_page.dart';
import 'package:cake_wallet/src/screens/address_book/edit_contact_group_page.dart';
import 'package:cake_wallet/src/screens/address_book/edit_contact_page.dart';
import 'package:cake_wallet/src/screens/address_book/contact_page.dart';
import 'package:cake_wallet/src/screens/address_book/edit_new_contact_group_page.dart';
import 'package:cake_wallet/src/screens/address_book/edit_new_contact_page.dart';
import 'package:cake_wallet/src/screens/address_book/entities/address_edit_request.dart';
import 'package:cake_wallet/src/screens/address_book/supported_handles_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
import 'package:cake_wallet/src/screens/address_book/contact_list_page.dart';
import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart';
import 'package:cake_wallet/src/screens/dev/moneroc_call_profiler.dart';
import 'package:cake_wallet/src/screens/dev/secure_preferences_page.dart';
@ -999,8 +998,8 @@ Future<void> setup({
),
);
getIt.registerFactoryParam<EditContactPage, ContactRecord, void>(
(contact, _) => EditContactPage(
getIt.registerFactoryParam<ContactPage, ContactRecord, void>(
(contact, _) => ContactPage(
contactViewModel: getIt.get<ContactViewModel>(
param1: AddressEditRequest.contact(contact),
),
@ -1035,13 +1034,6 @@ Future<void> setup({
},
);
getIt.registerFactoryParam<EditNewContactPage, ContactRecord?, void>(
(contact, _) => EditNewContactPage(
contactViewModel: getIt.get<ContactViewModel>(param1: AddressEditRequest.contact(contact)),
),
);
getIt.registerFactory(() => AddressListPage(getIt.get<WalletAddressListViewModel>()));
getIt.registerFactory(() {

View file

@ -26,7 +26,7 @@ import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/screens/connect_device/monero_hardware_wallet_options_page.dart';
import 'package:cake_wallet/src/screens/connect_device/select_hardware_wallet_account_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
import 'package:cake_wallet/src/screens/address_book/contact_list_page.dart';
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';

View file

@ -40,8 +40,7 @@ class Routes {
static const editContactGroupPage = '/edit_contact_group_page';
static const editAddressesPage = '/edit_addresses_page';
static const editAddressPage = '/edit_address_page';
static const editNewContactPage = '/edit_new_contact_page';
static const editContactPage = '/edit_contact_page';
static const contactPage = '/contact_page';
static const showKeys = '/show_keys';
static const exchangeConfirm = '/exchange_confirm';
static const tradeHistory = '/trade_history';

View file

@ -404,7 +404,7 @@ class _ContactListBodyState extends State<ContactListBody> {
onPressed: () async => await _showAddressBookBottomSheet(
context: context,
contactListViewModel: contactListViewModel,
initialRoute: Routes.editContactPage,
initialRoute: Routes.contactPage,
initialArgs: contact)),
],
),

View file

@ -14,11 +14,14 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class EditContactPage extends BasePage {
EditContactPage({required this.contactViewModel});
class ContactPage extends BasePage {
ContactPage({required this.contactViewModel});
final ContactViewModel contactViewModel;
@override
Widget? leading(BuildContext context) => null;
@override
Widget middle(BuildContext context) {
return Observer(

View file

@ -203,6 +203,17 @@ class _NewContactWelcomePageBodyState extends State<NewContactWelcomePageBody> {
: CustomThemeColors.backgroundGradientColorLight;
final isHandleMode = widget.handleOnly ? true : _mode == _InputMode.handle;
final hasDropdown = _results.isNotEmpty || _isSearching;
final radius = BorderRadius.vertical(
top: const Radius.circular(12),
bottom: hasDropdown ? Radius.zero : const Radius.circular(12),
);
final outline = OutlineInputBorder(
borderRadius: radius,
borderSide: BorderSide.none,
);
return StandardTextFormFieldWidget(
focusNode: _focusNode,
@ -211,13 +222,30 @@ class _NewContactWelcomePageBodyState extends State<NewContactWelcomePageBody> {
fillColor: fillColor,
onChanged: isHandleMode ? _handleChanged : (v) => setState(() => _typedAddress = v.trim()),
prefixIcon: isHandleMode
? null
? SizedBox(height: 50)
: Padding(
padding: const EdgeInsets.all(8.0),
child: _currencyPrefix(context),
),
prefixIconConstraints: isHandleMode
? const BoxConstraints(
minWidth: 12,
maxWidth: 12,
minHeight: 50,
maxHeight: 50,
)
: null,
suffixIconConstraints: const BoxConstraints(
minWidth: 32,
maxWidth: 40,
minHeight: 30,
maxHeight: 30,
),
suffixIcon: RoundedIconButton(
icon: Icons.paste_outlined,
iconSize: 20,
width: 38,
height: 36,
onPressed: () async {
final data = await Clipboard.getData(Clipboard.kTextPlain);
final text = data?.text?.trim() ?? '';
@ -236,6 +264,7 @@ class _NewContactWelcomePageBodyState extends State<NewContactWelcomePageBody> {
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(6))),
),
addressValidator: AddressValidator(type: _selectedCurrency),
outline: outline,
);
}

View file

@ -123,15 +123,15 @@ class EditNewContactGroupPage extends BasePage {
),
),
style: theme.textTheme.bodyMedium,
validator: (value) {
final text = value?.trim() ?? '';
if (text.isEmpty) return 'Name cannot be empty';
final clash = contactViewModel.box.values.any(
(c) => c.name.toLowerCase() == text.toLowerCase(),
);
return clash ? 'Group with this name already exists' : null;
},
// validator: (value) {
// final text = value?.trim() ?? '';
// if (text.isEmpty) return 'Name cannot be empty';
//
// final clash = contactViewModel.box.values.any(
// (c) => c.name.toLowerCase() == text.toLowerCase(),
// );
// return clash ? 'Group with this name already exists' : null;
// },
),
),
),
@ -185,7 +185,7 @@ class EditNewContactGroupPage extends BasePage {
if (context.mounted) {
Navigator.pushNamed(
context,
Routes.editNewContactPage,
Routes.contactPage,
arguments: record,
);
}

View file

@ -1,180 +0,0 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/address_book/entities/address_edit_request.dart';
import 'package:cake_wallet/src/screens/address_book/widgets/addresses_expansion_tile_widget.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/themes/utils/custom_theme_colors.dart';
import 'package:cake_wallet/utils/image_utill.dart';
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class EditNewContactPage extends BasePage {
EditNewContactPage({required this.contactViewModel});
final ContactViewModel contactViewModel;
Widget _circleIcon(BuildContext context, IconData icon, VoidCallback onPressed) {
final colorScheme = Theme.of(context).colorScheme;
return RawMaterialButton(
onPressed: onPressed,
fillColor: colorScheme.surfaceContainerHighest,
elevation: 0,
constraints: const BoxConstraints.tightFor(width: 24, height: 24),
padding: EdgeInsets.zero,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: const CircleBorder(),
child: Icon(icon, size: 14, color: colorScheme.onSurface),
);
}
@override
Widget leading(BuildContext context) {
return SizedBox(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_circleIcon(context, Icons.favorite_border_outlined, () {}),
const SizedBox(width: 8),
_circleIcon(context, Icons.refresh_sharp, () {}),
],
),
);
}
@override
Widget middle(BuildContext context) {
return Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Image(
width: 24,
height: 24,
image: contactViewModel.avatar,
fit: BoxFit.cover,
),
),
const SizedBox(width: 12),
Text(
contactViewModel.name,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontSize: 18,
fontWeight: FontWeight.w600,
color: titleColor(context),
),
),
],
),
);
}
@override
Widget trailing(BuildContext context) {
return SizedBox(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_circleIcon(context, Icons.add, () {}),
const SizedBox(width: 8),
_circleIcon(context, Icons.edit, () {}),
],
),
);
}
@override
Widget body(BuildContext context) {
final theme = Theme.of(context);
final fillColor = currentTheme.isDark
? CustomThemeColors.backgroundGradientColorDark.withAlpha(100)
: CustomThemeColors.backgroundGradientColorLight;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(8, 4, 8, 1),
decoration: BoxDecoration(
color: fillColor,
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ImageUtil.getImageFromPath(
imagePath: contactViewModel.sourceType.iconPath, height: 24, width: 24),
const SizedBox(width: 6),
Expanded(
child: Text(
contactViewModel.sourceType.label +
' - ' +
contactViewModel.handle,
style: theme.textTheme.labelSmall?.copyWith(
fontSize: 12,
color: theme.colorScheme.onSurfaceVariant,
),
),
),
const SizedBox(width: 6),
_circleIcon(context, Icons.edit, () {}),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Addresses detected:', style: theme.textTheme.bodyMedium),
const SizedBox(width: 8),
Expanded(
child: Wrap(
spacing: 8,
children: contactViewModel.parsedBlocks.values
.expand((map) => map.keys)
.toSet()
.map((CryptoCurrency cur) => cur.iconPath != null
? Image.asset(cur.iconPath!, height: 24, width: 24)
: const SizedBox.shrink())
.toList(),
),
),
],
),
),
],
),
),
const SizedBox(height: 8),
ContactAddressesExpansionTile(
key: ValueKey(contactViewModel.name),
title: Text('Manual Addresses'),
fillColor: fillColor,
manualByCurrency: contactViewModel.manual,
onCopyPressed: (address) async => await Clipboard.setData(ClipboardData(text: address)),
onEditPressed: (cur, lbl) {
Navigator.pushNamed(
context,
Routes.editAddressPage,
arguments: AddressEditRequest.address(
contact: contactViewModel.record,
currency: cur,
label: lbl,
kindIsManual: false,
)
);
},
),
],
),
);
}
}

View file

@ -12,6 +12,9 @@ class StandardTextFormFieldWidget extends StatelessWidget {
this.prefixIcon,
this.suffix,
this.onChanged,
this.suffixIconConstraints,
this.prefixIconConstraints,
this.outline,
});
final TextEditingController controller;
@ -22,7 +25,10 @@ class StandardTextFormFieldWidget extends StatelessWidget {
final Widget? suffixIcon;
final Widget? prefixIcon;
final Widget? suffix;
final BoxConstraints? suffixIconConstraints;
final BoxConstraints? prefixIconConstraints;
final void Function(String)? onChanged;
final InputBorder? outline;
@override
Widget build(BuildContext context) {
@ -39,22 +45,27 @@ class StandardTextFormFieldWidget extends StatelessWidget {
hintStyle:
Theme.of(context).textTheme.bodyMedium!.copyWith(color: Theme.of(context).hintColor),
fillColor: fillColor,
border: OutlineInputBorder(
border: outline ?? OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15)),
borderSide: BorderSide(color: Theme.of(context).colorScheme.outline)),
enabledBorder: OutlineInputBorder(
enabledBorder: outline ?? OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15)),
borderSide: BorderSide(color: Colors.transparent),
),
focusedBorder: outline?.copyWith(
borderSide: BorderSide(color: Theme.of(context).colorScheme.primary)
),
suffixIcon: Padding(padding: const EdgeInsets.only(right: 10), child: suffixIcon),
suffix: suffix,
prefixIcon: prefixIcon,
suffixIconConstraints: const BoxConstraints(
prefixIconConstraints: prefixIconConstraints,
suffixIconConstraints: suffixIconConstraints ?? const BoxConstraints(
minWidth: 34,
maxWidth: 34,
minHeight: 24,
maxHeight: 24,
)),
style: Theme.of(context).textTheme.bodyMedium,
onChanged: onChanged,
validator: addressValidator,

View file

@ -7,13 +7,10 @@ import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/address_book/contact_welcome_page.dart';
import 'package:cake_wallet/src/screens/address_book/edit_address_page.dart';
import 'package:cake_wallet/src/screens/address_book/edit_contact_group_page.dart';
import 'package:cake_wallet/src/screens/address_book/edit_contact_page.dart';
import 'package:cake_wallet/src/screens/address_book/contact_page.dart';
import 'package:cake_wallet/src/screens/address_book/edit_new_contact_group_page.dart';
import 'package:cake_wallet/src/screens/address_book/edit_new_contact_page.dart';
import 'package:cake_wallet/src/screens/address_book/entities/address_edit_request.dart';
import 'package:cake_wallet/src/screens/address_book/supported_handles_page.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
import 'package:flutter/material.dart';
@ -132,11 +129,8 @@ class _AddContactNavigator extends StatelessWidget {
final vm = args as ContactViewModel;
page = getIt<EditContactGroupPage>(param1: vm);
break;
case Routes.editNewContactPage:
page = getIt<EditNewContactPage>(param1: args as ContactRecord);
break;
case Routes.editContactPage:
page = getIt<EditContactPage>(param1: args as ContactRecord);
case Routes.contactPage:
page = getIt<ContactPage>(param1: args as ContactRecord);
break;
case Routes.editAddressPage:
page = getIt<EditAddressPage>(param1: args as AddressEditRequest);