diff --git a/lib/models/isar/models/blockchain_data/address.dart b/lib/models/isar/models/blockchain_data/address.dart index 0d29f28a7..770af4a6b 100644 --- a/lib/models/isar/models/blockchain_data/address.dart +++ b/lib/models/isar/models/blockchain_data/address.dart @@ -122,7 +122,26 @@ enum AddressType { cryptonote, mimbleWimble, unknown, - nonWallet, + nonWallet; + + String get readableName { + switch (this) { + case AddressType.p2pkh: + return "Legacy"; + case AddressType.p2sh: + return "Wrapped segwit"; + case AddressType.p2wpkh: + return "Segwit"; + case AddressType.cryptonote: + return "Cryptonote"; + case AddressType.mimbleWimble: + return "Mimble Wimble"; + case AddressType.unknown: + return "Unknown"; + case AddressType.nonWallet: + return "Non wallet/unknown"; + } + } } // do not modify @@ -133,7 +152,26 @@ enum AddressSubType { paynymSend, paynymReceive, unknown, - nonWallet, + nonWallet; + + String get prettyName { + switch (this) { + case AddressSubType.receiving: + return "Receiving"; + case AddressSubType.change: + return "Change"; + case AddressSubType.paynymNotification: + return "PayNym Notification"; + case AddressSubType.paynymSend: + return "PayNym Send"; + case AddressSubType.paynymReceive: + return "PayNym Receiving"; + case AddressSubType.unknown: + return "Unknown"; + case AddressSubType.nonWallet: + return "Non wallet/unknown"; + } + } } @Embedded(inheritance: false) diff --git a/lib/pages/generic/single_field_edit_view.dart b/lib/pages/generic/single_field_edit_view.dart index c1049b006..5735a7754 100644 --- a/lib/pages/generic/single_field_edit_view.dart +++ b/lib/pages/generic/single_field_edit_view.dart @@ -196,7 +196,11 @@ class _SingleFieldEditViewState extends State { } }, ), - ) + ), + if (!isDesktop) + const SizedBox( + height: 16, + ), ], ), ); diff --git a/lib/pages/receive_view/addresses/address_card.dart b/lib/pages/receive_view/addresses/address_card.dart index 3f6edb585..f99db972d 100644 --- a/lib/pages/receive_view/addresses/address_card.dart +++ b/lib/pages/receive_view/addresses/address_card.dart @@ -1,21 +1,13 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:isar/isar.dart'; import 'package:stackwallet/db/main_db.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; -import 'package:stackwallet/notifications/show_flush_bar.dart'; -import 'package:stackwallet/pages/receive_view/addresses/address_qr_popup.dart'; -import 'package:stackwallet/pages/receive_view/addresses/edit_address_label_view.dart'; +import 'package:stackwallet/pages/receive_view/addresses/address_tag.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/text_styles.dart'; -import 'package:stackwallet/utilities/theme/stack_colors.dart'; -import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; -import 'package:stackwallet/widgets/desktop/secondary_button.dart'; -import 'package:stackwallet/widgets/icon_widgets/copy_icon.dart'; -import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; class AddressCard extends StatefulWidget { @@ -24,6 +16,7 @@ class AddressCard extends StatefulWidget { required this.addressId, required this.walletId, required this.coin, + this.onPressed, this.clipboard = const ClipboardWrapper(), }) : super(key: key); @@ -31,6 +24,7 @@ class AddressCard extends StatefulWidget { final String walletId; final Coin coin; final ClipboardInterface clipboard; + final VoidCallback? onPressed; @override State createState() => _AddressCardState(); @@ -72,106 +66,126 @@ class _AddressCardState extends State { @override Widget build(BuildContext context) { return RoundedWhiteContainer( + onPressed: widget.onPressed, child: StreamBuilder( - stream: stream, - builder: (context, snapshot) { - if (snapshot.hasData) { - label = snapshot.data!; - } + stream: stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + label = snapshot.data!; + } - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - label!.value, - style: STextStyles.itemSubtitle(context), - ), - CustomTextButton( - text: "Edit label", - textSize: 14, - onTap: () { - Navigator.of(context).pushNamed( - EditAddressLabelView.routeName, - arguments: label!.id, - ); - }, - ), - ], + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (label!.value.isNotEmpty) + Text( + label!.value, + style: STextStyles.itemSubtitle(context), + textAlign: TextAlign.left, ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // + // CustomTextButton( + // text: "Edit label", + // textSize: 14, + // onTap: () { + // Navigator.of(context).pushNamed( + // EditAddressLabelView.routeName, + // arguments: label!.id, + // ); + // }, + // ), + // ], + // ), + if (label!.value.isNotEmpty) const SizedBox( height: 8, ), - Row( - children: [ - Expanded( - child: SelectableText( - address.value, - style: STextStyles.itemSubtitle12(context), - ), - ) - ], - ), - const SizedBox( - height: 10, - ), - Row( - children: [ - Expanded( - child: SecondaryButton( - label: "Copy address", - icon: CopyIcon( - color: Theme.of(context) - .extension()! - .buttonTextSecondary, + Row( + children: [ + Expanded( + child: SelectableText( + address.value, + style: STextStyles.itemSubtitle12(context), + ), + ) + ], + ), + const SizedBox( + height: 10, + ), + + if (label!.tags != null && label!.tags!.isNotEmpty) + Wrap( + spacing: 10, + runSpacing: 10, + children: label!.tags! + .map( + (e) => AddressTag( + tag: e, ), - onPressed: () async { - await widget.clipboard.setData( - ClipboardData( - text: address.value, - ), - ); - if (mounted) { - unawaited( - showFloatingFlushBar( - type: FlushBarType.info, - message: "Copied to clipboard", - context: context, - ), - ); - } - }, - ), - ), - const SizedBox( - width: 12, - ), - Expanded( - child: SecondaryButton( - label: "Show QR Code", - icon: QrCodeIcon( - color: Theme.of(context) - .extension()! - .buttonTextSecondary, - ), - onPressed: () { - showDialog( - context: context, - builder: (context) => AddressQrPopup( - addressString: address.value, - coin: widget.coin, - clipboard: widget.clipboard, - ), - ); - }, - ), - ), - ], - ) - ], - ); - }), + ) + .toList(), + ), + // Row( + // children: [ + // Expanded( + // child: SecondaryButton( + // label: "Copy address", + // icon: CopyIcon( + // color: Theme.of(context) + // .extension()! + // .buttonTextSecondary, + // ), + // onPressed: () async { + // await widget.clipboard.setData( + // ClipboardData( + // text: address.value, + // ), + // ); + // if (mounted) { + // unawaited( + // showFloatingFlushBar( + // type: FlushBarType.info, + // message: "Copied to clipboard", + // context: context, + // ), + // ); + // } + // }, + // ), + // ), + // const SizedBox( + // width: 12, + // ), + // Expanded( + // child: SecondaryButton( + // label: "Show QR Code", + // icon: QrCodeIcon( + // color: Theme.of(context) + // .extension()! + // .buttonTextSecondary, + // ), + // onPressed: () { + // showDialog( + // context: context, + // builder: (context) => AddressQrPopup( + // addressString: address.value, + // coin: widget.coin, + // clipboard: widget.clipboard, + // ), + // ); + // }, + // ), + // ), + // ], + // ) + ], + ); + }, + ), ); } } diff --git a/lib/pages/receive_view/addresses/address_details_view.dart b/lib/pages/receive_view/addresses/address_details_view.dart new file mode 100644 index 000000000..095f202ae --- /dev/null +++ b/lib/pages/receive_view/addresses/address_details_view.dart @@ -0,0 +1,346 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:isar/isar.dart'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:stackwallet/db/main_db.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; +import 'package:stackwallet/pages/receive_view/addresses/address_tag.dart'; +import 'package:stackwallet/pages/wallet_view/sub_widgets/transactions_list.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/utilities/address_utils.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/custom_buttons/simple_copy_button.dart'; +import 'package:stackwallet/widgets/custom_buttons/simple_edit_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class AddressDetailsView extends ConsumerStatefulWidget { + const AddressDetailsView({ + Key? key, + required this.addressId, + required this.walletId, + }) : super(key: key); + + static const String routeName = "/addressDetailsView"; + + final Id addressId; + final String walletId; + + @override + ConsumerState createState() => _AddressDetailsViewState(); +} + +class _AddressDetailsViewState extends ConsumerState { + final _qrKey = GlobalKey(); + final isDesktop = Util.isDesktop; + + late Stream stream; + late final Address address; + + AddressLabel? label; + + @override + void initState() { + address = MainDB.instance.isar.addresses + .where() + .idEqualTo(widget.addressId) + .findFirstSync()!; + + label = MainDB.instance.getAddressLabelSync(widget.walletId, address.value); + Id? id = label?.id; + if (id == null) { + label = AddressLabel( + walletId: widget.walletId, + addressString: address.value, + value: "", + tags: address.subType == AddressSubType.receiving + ? ["receiving"] + : address.subType == AddressSubType.change + ? ["change"] + : null, + ); + id = MainDB.instance.putAddressLabelSync(label!); + } + stream = MainDB.instance.watchAddressLabel(id: id); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + final coin = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId).coin)); + return ConditionalParent( + condition: !isDesktop, + builder: (child) => Background( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + backgroundColor: + Theme.of(context).extension()!.backgroundAppBar, + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + titleSpacing: 0, + title: Text( + "Wallet addresses", + style: STextStyles.navBarTitle(context), + ), + ), + body: SafeArea( + child: NestedScrollView( + floatHeaderSlivers: true, + headerSliverBuilder: (context, innerBoxIsScrolled) { + return [ + SliverOverlapAbsorber( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor( + context), + sliver: SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only( + left: 16, + right: 16, + top: 16, + ), + child: child, + ), + ), + ) + ]; + }, + body: TransactionsList( + walletId: widget.walletId, + managerProvider: ref.watch( + walletsChangeNotifierProvider.select((value) => + value.getManagerProvider(widget.walletId)))), + ), + ), + ), + ), + child: StreamBuilder( + stream: stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + label = snapshot.data!; + } + + return Column( + children: [ + Center( + child: RepaintBoundary( + key: _qrKey, + child: QrImage( + data: AddressUtils.buildUriString( + coin, + address.value, + {}, + ), + size: 220, + backgroundColor: + Theme.of(context).extension()!.background, + foregroundColor: Theme.of(context) + .extension()! + .accentColorDark, + ), + ), + ), + const SizedBox( + height: 16, + ), + _Item( + title: "Address", + data: address.value, + button: SimpleCopyButton( + data: address.value, + ), + ), + const SizedBox( + height: 12, + ), + _Item( + title: "Label", + data: label!.value, + button: SimpleEditButton( + editValue: label!.value, + editLabel: 'label', + onValueChanged: (value) { + MainDB.instance.putAddressLabel( + label!.copyWith( + label: value, + ), + ); + }, + ), + ), + const SizedBox( + height: 12, + ), + _Tags( + tags: label!.tags, + ), + if (address.derivationPath != null) + const SizedBox( + height: 12, + ), + if (address.derivationPath != null) + _Item( + title: "Derivation path", + data: address.derivationPath!.value, + button: SimpleEditButton( + editValue: label!.value, + editLabel: 'label', + onValueChanged: (value) { + MainDB.instance.putAddressLabel( + label!.copyWith( + label: value, + ), + ); + }, + ), + ), + const SizedBox( + height: 12, + ), + _Item( + title: "Type", + data: address.type.readableName, + button: Container(), + ), + const SizedBox( + height: 12, + ), + _Item( + title: "Sub type", + data: address.subType.prettyName, + button: Container(), + ), + const SizedBox( + height: 20, + ), + Text( + "Transactions", + style: STextStyles.itemSubtitle(context).copyWith( + color: Theme.of(context).extension()!.textDark3, + ), + ), + ], + ); + }, + ), + ); + } +} + +class _Tags extends StatelessWidget { + const _Tags({ + Key? key, + required this.tags, + }) : super(key: key); + + final List? tags; + + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Tags", + style: STextStyles.itemSubtitle(context), + ), + SimpleEditButton( + onPressedOverride: () { + // TODO edit tags + }, + ), + ], + ), + const SizedBox( + height: 8, + ), + tags != null && tags!.isNotEmpty + ? Wrap( + spacing: 10, + runSpacing: 10, + children: tags! + .map( + (e) => AddressTag( + tag: e, + ), + ) + .toList(), + ) + : Text( + "Tags will appear here", + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle3, + ), + ), + ], + ), + ); + } +} + +class _Item extends StatelessWidget { + const _Item({ + Key? key, + required this.title, + required this.data, + required this.button, + }) : super(key: key); + + final String title; + final String data; + final Widget button; + + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: STextStyles.itemSubtitle(context), + ), + button, + ], + ), + const SizedBox( + height: 5, + ), + data.isNotEmpty + ? SelectableText( + data, + style: STextStyles.w500_14(context), + ) + : Text( + "$title will appear here", + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle3, + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/receive_view/addresses/address_tag.dart b/lib/pages/receive_view/addresses/address_tag.dart new file mode 100644 index 000000000..df03dc405 --- /dev/null +++ b/lib/pages/receive_view/addresses/address_tag.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_native_splash/cli_commands.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/rounded_container.dart'; + +class AddressTag extends StatelessWidget { + const AddressTag({Key? key, required this.tag}) : super(key: key); + + final String tag; + + @override + Widget build(BuildContext context) { + return RoundedContainer( + radiusMultiplier: 0.5, + padding: const EdgeInsets.symmetric( + vertical: 5, + horizontal: 7, + ), + color: Colors.black, + child: Text( + tag.capitalize(), + style: STextStyles.w500_14(context), + ), + ); + } +} diff --git a/lib/pages/receive_view/addresses/wallet_addresses_view.dart b/lib/pages/receive_view/addresses/wallet_addresses_view.dart index aec8f24c3..aa0948528 100644 --- a/lib/pages/receive_view/addresses/wallet_addresses_view.dart +++ b/lib/pages/receive_view/addresses/wallet_addresses_view.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:isar/isar.dart'; import 'package:stackwallet/db/main_db.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/pages/receive_view/addresses/address_card.dart'; +import 'package:stackwallet/pages/receive_view/addresses/address_details_view.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/text_styles.dart'; @@ -13,7 +15,12 @@ import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/loading_indicator.dart'; -import 'package:stackwallet/widgets/toggle.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; +import 'package:tuple/tuple.dart'; + +import '../../../utilities/assets.dart'; +import '../../../widgets/icon_widgets/x_icon.dart'; +import '../../../widgets/textfield_icon_button.dart'; class WalletAddressesView extends ConsumerStatefulWidget { const WalletAddressesView({ @@ -33,7 +40,88 @@ class WalletAddressesView extends ConsumerStatefulWidget { class _WalletAddressesViewState extends ConsumerState { final bool isDesktop = Util.isDesktop; - bool _showChange = false; + String _searchString = ""; + + late final TextEditingController _searchController; + final searchFieldFocusNode = FocusNode(); + + Future> _search(String term) async { + if (term.isEmpty) { + return MainDB.instance + .getAddresses(widget.walletId) + .filter() + .group((q) => q + .subTypeEqualTo(AddressSubType.change) + .or() + .subTypeEqualTo(AddressSubType.receiving) + .or() + .subTypeEqualTo(AddressSubType.paynymReceive) + .or() + .subTypeEqualTo(AddressSubType.paynymNotification)) + .and() + .not() + .typeEqualTo(AddressType.nonWallet) + .sortByDerivationIndex() + .idProperty() + .findAll(); + } + + final labels = await MainDB.instance + .getAddressLabels(widget.walletId) + .filter() + .group( + (q) => q + .valueContains(term, caseSensitive: false) + .or() + .addressStringContains(term, caseSensitive: false) + .or() + .group( + (q) => q + .tagsIsNotNull() + .and() + .tagsElementContains(term, caseSensitive: false), + ), + ) + .findAll(); + + if (labels.isEmpty) { + return []; + } + + return MainDB.instance + .getAddresses(widget.walletId) + .filter() + .anyOf( + labels, (q, e) => q.valueEqualTo(e.addressString)) + .group((q) => q + .subTypeEqualTo(AddressSubType.change) + .or() + .subTypeEqualTo(AddressSubType.receiving) + .or() + .subTypeEqualTo(AddressSubType.paynymReceive) + .or() + .subTypeEqualTo(AddressSubType.paynymNotification)) + .and() + .not() + .typeEqualTo(AddressType.nonWallet) + .sortByDerivationIndex() + .idProperty() + .findAll(); + } + + @override + void initState() { + _searchController = TextEditingController(); + + super.initState(); + } + + @override + void dispose() { + _searchController.dispose(); + searchFieldFocusNode.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -68,26 +156,66 @@ class _WalletAddressesViewState extends ConsumerState { child: Column( children: [ SizedBox( - height: isDesktop ? 56 : 48, width: isDesktop ? 490 : null, - child: Toggle( - key: UniqueKey(), - onColor: Theme.of(context).extension()!.popupBG, - onText: "Receiving", - offColor: Theme.of(context) - .extension()! - .textFieldDefaultBG, - offText: "Change", - isOn: _showChange, - onValueChanged: (value) { - setState(() { - _showChange = value; - }); - }, - decoration: BoxDecoration( - color: Colors.transparent, - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: !isDesktop, + enableSuggestions: !isDesktop, + controller: _searchController, + focusNode: searchFieldFocusNode, + onChanged: (value) { + setState(() { + _searchString = value; + }); + }, + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context) + .extension()! + .textFieldActiveText, + height: 1.8, + ) + : STextStyles.field(context), + decoration: standardInputDecoration( + "Search...", + searchFieldFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + prefixIcon: Padding( + padding: EdgeInsets.symmetric( + horizontal: isDesktop ? 12 : 10, + vertical: isDesktop ? 18 : 16, + ), + child: SvgPicture.asset( + Assets.svg.search, + width: isDesktop ? 20 : 16, + height: isDesktop ? 20 : 16, + ), + ), + suffixIcon: _searchController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _searchController.text = ""; + _searchString = ""; + }); + }, + ), + ], + ), + ), + ) + : null, ), ), ), @@ -97,23 +225,7 @@ class _WalletAddressesViewState extends ConsumerState { ), Expanded( child: FutureBuilder( - future: MainDB.instance - .getAddresses(widget.walletId) - .filter() - .group( - (q) => _showChange - ? q.subTypeEqualTo(AddressSubType.change) - : q - .subTypeEqualTo(AddressSubType.receiving) - .or() - .subTypeEqualTo(AddressSubType.paynymReceive), - ) - .and() - .not() - .typeEqualTo(AddressType.nonWallet) - .sortByDerivationIndex() - .idProperty() - .findAll(), + future: _search(_searchString), builder: (context, AsyncSnapshot> snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) { @@ -127,6 +239,15 @@ class _WalletAddressesViewState extends ConsumerState { walletId: widget.walletId, addressId: snapshot.data![index], coin: coin, + onPressed: () { + Navigator.of(context).pushNamed( + AddressDetailsView.routeName, + arguments: Tuple2( + snapshot.data![index], + widget.walletId, + ), + ); + }, ), ); } else { diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 9741885a1..524639e54 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -48,6 +48,7 @@ import 'package:stackwallet/pages/paynym/add_new_paynym_follow_view.dart'; import 'package:stackwallet/pages/paynym/paynym_claim_view.dart'; import 'package:stackwallet/pages/paynym/paynym_home_view.dart'; import 'package:stackwallet/pages/pinpad_views/create_pin_view.dart'; +import 'package:stackwallet/pages/receive_view/addresses/address_details_view.dart'; import 'package:stackwallet/pages/receive_view/addresses/edit_address_label_view.dart'; import 'package:stackwallet/pages/receive_view/addresses/wallet_addresses_view.dart'; import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart'; @@ -928,6 +929,21 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case AddressDetailsView.routeName: + if (args is Tuple2) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => AddressDetailsView( + walletId: args.item2, + addressId: args.item1, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + case SendView.routeName: if (args is Tuple2) { return getRoute( diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index c3bd082da..aaa40b45a 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -221,11 +221,27 @@ class DbVersionMigrator with WalletDB { final List labels = []; for (final address in addresses) { - final tags = address.subType == AddressSubType.receiving - ? ["receiving"] - : address.subType == AddressSubType.change - ? ["change"] - : null; + List? tags; + switch (address.subType) { + case AddressSubType.receiving: + tags = ["receiving"]; + break; + case AddressSubType.change: + tags = ["change"]; + break; + case AddressSubType.paynymNotification: + tags = ["paynym notification"]; + break; + case AddressSubType.paynymSend: + break; + case AddressSubType.paynymReceive: + tags = ["paynym receiving"]; + break; + case AddressSubType.unknown: + break; + case AddressSubType.nonWallet: + break; + } // update/create label if tags is not empty if (tags != null) { diff --git a/lib/widgets/custom_buttons/simple_edit_button.dart b/lib/widgets/custom_buttons/simple_edit_button.dart index 931de7aa4..3bebeb4d0 100644 --- a/lib/widgets/custom_buttons/simple_edit_button.dart +++ b/lib/widgets/custom_buttons/simple_edit_button.dart @@ -13,14 +13,23 @@ import '../icon_widgets/pencil_icon.dart'; class SimpleEditButton extends StatelessWidget { const SimpleEditButton({ Key? key, - required this.editValue, - required this.editLabel, - required this.onValueChanged, - }) : super(key: key); + this.editValue, + this.editLabel, + this.onValueChanged, + this.onPressedOverride, + }) : assert( + (editLabel != null && editValue != null && onValueChanged != null) || + (editLabel == null && + editValue == null && + onValueChanged == null && + onPressedOverride != null), + ), + super(key: key); - final String editValue; - final String editLabel; - final void Function(String) onValueChanged; + final String? editValue; + final String? editLabel; + final void Function(String)? onValueChanged; + final VoidCallback? onPressedOverride; @override Widget build(BuildContext context) { @@ -36,24 +45,25 @@ class SimpleEditButton extends StatelessWidget { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6), ), - onPressed: () async { - final result = await showDialog( - context: context, - builder: (context) { - return DesktopDialog( - maxWidth: 580, - maxHeight: 360, - child: SingleFieldEditView( - initialValue: editValue, - label: editLabel, - ), + onPressed: onPressedOverride ?? + () async { + final result = await showDialog( + context: context, + builder: (context) { + return DesktopDialog( + maxWidth: 580, + maxHeight: 360, + child: SingleFieldEditView( + initialValue: editValue!, + label: editLabel!, + ), + ); + }, ); + if (result is String && result != editValue!) { + onValueChanged?.call(result); + } }, - ); - if (result is String && result != editValue) { - onValueChanged(result); - } - }, child: Padding( padding: const EdgeInsets.all(5), child: PencilIcon( @@ -66,18 +76,19 @@ class SimpleEditButton extends StatelessWidget { ); } else { return GestureDetector( - onTap: () async { - final result = await Navigator.of(context).pushNamed( - SingleFieldEditView.routeName, - arguments: Tuple2( - editValue, - editLabel, - ), - ); - if (result is String && result != editValue) { - onValueChanged(result); - } - }, + onTap: onPressedOverride ?? + () async { + final result = await Navigator.of(context).pushNamed( + SingleFieldEditView.routeName, + arguments: Tuple2( + editValue!, + editLabel!, + ), + ); + if (result is String && result != editValue!) { + onValueChanged?.call(result); + } + }, child: Row( children: [ SvgPicture.asset( diff --git a/lib/widgets/rounded_white_container.dart b/lib/widgets/rounded_white_container.dart index 2ade9b729..8208d69ca 100644 --- a/lib/widgets/rounded_white_container.dart +++ b/lib/widgets/rounded_white_container.dart @@ -12,6 +12,7 @@ class RoundedWhiteContainer extends StatelessWidget { this.height, this.borderColor, this.boxShadow, + this.onPressed, }) : super(key: key); final Widget? child; @@ -21,6 +22,7 @@ class RoundedWhiteContainer extends StatelessWidget { final double? height; final Color? borderColor; final List? boxShadow; + final VoidCallback? onPressed; @override Widget build(BuildContext context) { @@ -32,6 +34,7 @@ class RoundedWhiteContainer extends StatelessWidget { height: height, borderColor: borderColor, boxShadow: boxShadow, + onPressed: onPressed, child: child, ); }