diff --git a/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_details.dart b/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_details.dart index 2cd20a839..96eed4835 100644 --- a/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_details.dart +++ b/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_details.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:stackwallet/models/contact.dart'; import 'package:stackwallet/models/paymint/transactions_model.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/address_book_views/subviews/add_new_contact_address_view.dart'; import 'package:stackwallet/pages_desktop_specific/home/address_book_view/subwidgets/desktop_address_card.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; @@ -15,6 +16,8 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/loading_indicator.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; @@ -74,8 +77,16 @@ class _DesktopContactDetailsState extends ConsumerState { @override Widget build(BuildContext context) { - final contact = ref.watch(addressBookServiceProvider - .select((value) => value.getContactById(widget.contactId))); + // provider hack to prevent trying to update widget with deleted contact + Contact? _contact; + try { + _contact = ref.watch(addressBookServiceProvider + .select((value) => value.getContactById(widget.contactId))); + } catch (_) { + return Container(); + } + + final contact = _contact!; return Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -129,22 +140,23 @@ class _DesktopContactDetailsState extends ConsumerState { ), ], ), - SecondaryButton( - label: "Options", - width: 96, - buttonHeight: ButtonHeight.xxs, - onPressed: () async { - await showDialog( - context: context, - barrierColor: Colors.transparent, - builder: (context) { - return DesktopContactOptionsMenuPopup( - contactId: contact.id, - ); - }, - ); - }, - ), + if (widget.contactId != "default") + SecondaryButton( + label: "Options", + width: 96, + buttonHeight: ButtonHeight.xxs, + onPressed: () async { + await showDialog( + context: context, + barrierColor: Colors.transparent, + builder: (context) { + return DesktopContactOptionsMenuPopup( + contactId: contact.id, + ); + }, + ); + }, + ), ], ), const SizedBox( @@ -453,132 +465,238 @@ class _DesktopContactOptionsMenuPopupState ), ), ), - const SizedBox( - height: 2, - ), - MouseRegion( - onEnter: (_) { - setState(() { - hoveredOnPencil = true; - }); - }, - onExit: (_) { - setState(() { - hoveredOnPencil = false; - }); - }, - child: RawMaterialButton( - hoverColor: Theme.of(context) - .extension()! - .textFieldDefaultBG, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 1000, - ), - ), - onPressed: () { - print("should go to edit"); + if (widget.contactId != "default") + const SizedBox( + height: 2, + ), + if (widget.contactId != "default") + MouseRegion( + onEnter: (_) { + setState(() { + hoveredOnPencil = true; + }); }, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 25, - vertical: 16, + onExit: (_) { + setState(() { + hoveredOnPencil = false; + }); + }, + child: RawMaterialButton( + hoverColor: Theme.of(context) + .extension()! + .textFieldDefaultBG, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 1000, + ), ), - child: Row( - children: [ - SvgPicture.asset( - Assets.svg.pencil, - width: 24, - height: 22, - color: hoveredOnPencil - ? Theme.of(context) - .extension()! - .textDark - : Theme.of(context) - .extension()! - .textFieldDefaultSearchIconLeft, - ), - const SizedBox( - width: 12, - ), - Text( - "Edit contact", - style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark, + onPressed: () { + print("should go to edit"); + }, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 25, + vertical: 16, + ), + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.pencil, + width: 24, + height: 22, + color: hoveredOnPencil + ? Theme.of(context) + .extension()! + .textDark + : Theme.of(context) + .extension()! + .textFieldDefaultSearchIconLeft, ), - ) - ], + const SizedBox( + width: 12, + ), + Text( + "Edit contact", + style: STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + ) + ], + ), ), ), ), - ), - const SizedBox( - height: 2, - ), - MouseRegion( - onEnter: (_) { - setState(() { - hoveredOnTrash = true; - }); - }, - onExit: (_) { - setState(() { - hoveredOnTrash = false; - }); - }, - child: RawMaterialButton( - hoverColor: Theme.of(context) - .extension()! - .textFieldDefaultBG, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 1000, - ), - ), - onPressed: () { - print("should delete contact"); + if (widget.contactId != "default") + const SizedBox( + height: 2, + ), + if (widget.contactId != "default") + MouseRegion( + onEnter: (_) { + setState(() { + hoveredOnTrash = true; + }); }, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 25, - vertical: 16, + onExit: (_) { + setState(() { + hoveredOnTrash = false; + }); + }, + child: RawMaterialButton( + hoverColor: Theme.of(context) + .extension()! + .textFieldDefaultBG, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 1000, + ), ), - child: Row( - children: [ - SvgPicture.asset( - Assets.svg.trash, - width: 24, - height: 22, - color: hoveredOnTrash - ? Theme.of(context) - .extension()! - .textDark - : Theme.of(context) - .extension()! - .textFieldDefaultSearchIconLeft, - ), - const SizedBox( - width: 12, - ), - Text( - "Delete contact", - style: STextStyles.desktopTextExtraExtraSmall( - context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark, + onPressed: () { + final contact = ref + .read(addressBookServiceProvider) + .getContactById(widget.contactId); + + // pop context menu + Navigator.of(context).pop(); + + showDialog( + context: context, + useSafeArea: true, + barrierDismissible: true, + builder: (_) => DesktopDialog( + maxWidth: 500, + maxHeight: 300, + child: Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 32, + ), + child: Text( + "Delete ${contact.name}?", + style: STextStyles.desktopH3(context), + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only( + left: 32, + right: 32, + bottom: 32, + ), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Spacer( + flex: 1, + ), + Text( + "Contact will be deleted permanently!", + style: STextStyles.desktopTextSmall( + context), + ), + const Spacer( + flex: 2, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Cancel", + onPressed: + Navigator.of(context).pop, + buttonHeight: ButtonHeight.l, + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: Consumer( + builder: (context, ref, __) => + PrimaryButton( + label: "Delete", + buttonHeight: + ButtonHeight.l, + onPressed: () { + ref + .read( + addressBookServiceProvider) + .removeContact( + contact.id); + Navigator.of(context) + .pop(); + showFloatingFlushBar( + type: FlushBarType + .success, + message: + "${contact.name} deleted", + context: context, + ); + }, + ), + ), + ), + ], + ) + ], + ), + ), + ), + ], ), - ) - ], + ), + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 25, + vertical: 16, + ), + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.trash, + width: 24, + height: 22, + color: hoveredOnTrash + ? Theme.of(context) + .extension()! + .textDark + : Theme.of(context) + .extension()! + .textFieldDefaultSearchIconLeft, + ), + const SizedBox( + width: 12, + ), + Text( + "Delete contact", + style: STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + ) + ], + ), ), ), ), - ), ], ), ), diff --git a/lib/widgets/address_book_card.dart b/lib/widgets/address_book_card.dart index b79f89662..dfa655f86 100644 --- a/lib/widgets/address_book_card.dart +++ b/lib/widgets/address_book_card.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/models/contact.dart'; import 'package:stackwallet/pages/address_book_views/subviews/contact_popup.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; import 'package:stackwallet/utilities/assets.dart'; @@ -44,10 +45,16 @@ class _AddressBookCardState extends ConsumerState { @override Widget build(BuildContext context) { - // final isTiny = SizingUtilities.isTinyWidth(context); + // provider hack to prevent trying to update widget with deleted contact + Contact? _contact; + try { + _contact = ref.watch(addressBookServiceProvider + .select((value) => value.getContactById(contactId))); + } catch (_) { + return Container(); + } - final contact = ref.watch(addressBookServiceProvider - .select((value) => value.getContactById(contactId))); + final contact = _contact!; final List coins = []; for (var element in contact.addresses) {