From d4b7ec0f174101e406796f4a2bc6162a0c9f0f58 Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 18 Nov 2022 09:54:26 -0600 Subject: [PATCH] desktop edit contact --- .../edit_contact_name_emoji_view.dart | 576 ++++++++++-------- .../desktop_contact_options_menu_popup.dart | 252 ++++---- 2 files changed, 462 insertions(+), 366 deletions(-) diff --git a/lib/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart b/lib/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart index fff01eee3..a9b264b3c 100644 --- a/lib/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart +++ b/lib/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:emojis/emoji.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -7,14 +9,17 @@ import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.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/conditional_parent.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/emoji_select_sheet.dart'; import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart'; -import 'package:stackwallet/utilities/util.dart'; - class EditContactNameEmojiView extends ConsumerStatefulWidget { const EditContactNameEmojiView({ Key? key, @@ -69,268 +74,323 @@ class _EditContactNameEmojiViewState final contact = ref.watch(addressBookServiceProvider .select((value) => value.getContactById(contactId))); - return Scaffold( - backgroundColor: Theme.of(context).extension()!.background, - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () async { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed(const Duration(milliseconds: 75)); - } - if (mounted) { - Navigator.of(context).pop(); - } - }, - ), - title: Text( - "Edit contact", - style: STextStyles.navBarTitle(context), - ), - ), - body: LayoutBuilder( - builder: (context, constraints) { - return Padding( - padding: const EdgeInsets.only( - left: 12, - top: 12, - right: 12, - ), - child: SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight - 24, - ), - child: IntrinsicHeight( - child: Padding( - padding: const EdgeInsets.all(4), - child: Column( - children: [ - GestureDetector( - onTap: () { - if (_selectedEmoji != null) { - setState(() { - _selectedEmoji = null; - }); - return; - } - showModalBottomSheet( - backgroundColor: Colors.transparent, - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.circular(20), - ), - ), - builder: (_) => const EmojiSelectSheet(), - ).then((value) { - if (value is Emoji) { - setState(() { - _selectedEmoji = value; - }); - } - }); - }, - child: SizedBox( - height: 48, - width: 48, - child: Stack( - children: [ - Container( - height: 48, - width: 48, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(24), - color: Theme.of(context) - .extension()! - .textFieldActiveBG, - ), - child: Center( - child: _selectedEmoji == null - ? SvgPicture.asset( - Assets.svg.user, - height: 24, - width: 24, - ) - : Text( - _selectedEmoji!.char, - style: STextStyles.pageTitleH1( - context), - ), - ), - ), - Align( - alignment: Alignment.bottomRight, - child: Container( - height: 14, - width: 14, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(14), - color: Theme.of(context) - .extension()! - .accentColorDark), - child: Center( - child: _selectedEmoji == null - ? SvgPicture.asset( - Assets.svg.plus, - color: Theme.of(context) - .extension()! - .textWhite, - width: 12, - height: 12, - ) - : SvgPicture.asset( - Assets.svg.thickX, - color: Theme.of(context) - .extension()! - .textWhite, - width: 8, - height: 8, - ), - ), - ), - ) - ], - ), - ), - ), - const SizedBox( - height: 8, - ), - ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - child: TextField( - autocorrect: Util.isDesktop ? false : true, - enableSuggestions: Util.isDesktop ? false : true, - controller: nameController, - focusNode: nameFocusNode, - style: STextStyles.field(context), - onChanged: (_) => setState(() {}), - decoration: standardInputDecoration( - "Enter contact name", - nameFocusNode, - context, - ).copyWith( - suffixIcon: nameController.text.isNotEmpty - ? Padding( - padding: const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState(() { - nameController.text = ""; - }); - }, - ), - ], - ), - ), - ) - : null, - ), - ), - ), - const Spacer(), - const SizedBox( - height: 16, - ), - Row( - children: [ - Expanded( - child: TextButton( - style: Theme.of(context) - .extension()! - .getSecondaryEnabledButtonColor(context), - child: Text( - "Cancel", - style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), - ), - onPressed: () async { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 75)); - } - if (mounted) { - Navigator.of(context).pop(); - } - }, - ), - ), - const SizedBox( - width: 16, - ), - Expanded( - child: Builder( - builder: (context) { - bool shouldEnableSave = - nameController.text.isNotEmpty; + final isDesktop = Util.isDesktop; + final double emojiSize = isDesktop ? 56 : 48; - return TextButton( - style: shouldEnableSave - ? Theme.of(context) - .extension()! - .getPrimaryEnabledButtonColor( - context) - : Theme.of(context) - .extension()! - .getPrimaryDisabledButtonColor( - context), - onPressed: shouldEnableSave - ? () async { - if (FocusScope.of(context) - .hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration( - milliseconds: 75), - ); - } - final editedContact = - contact.copyWith( - shouldCopyEmojiWithNull: true, - name: nameController.text, - emojiChar: _selectedEmoji == null - ? null - : _selectedEmoji!.char, - ); - ref - .read( - addressBookServiceProvider) - .editContact( - editedContact, - ); - if (mounted) { - Navigator.of(context).pop(); - } - } - : null, - child: Text( - "Save", - style: STextStyles.button(context), - ), - ); - }, - ), - ), - ], - ) - ], + return ConditionalParent( + condition: !isDesktop, + builder: (child) => Scaffold( + backgroundColor: Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed(const Duration(milliseconds: 75)); + } + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + title: Text( + "Edit contact", + style: STextStyles.navBarTitle(context), + ), + ), + body: LayoutBuilder( + builder: (context, constraints) { + return Padding( + padding: const EdgeInsets.only( + left: 12, + top: 12, + right: 12, + ), + child: SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight - 24, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(4), + child: child, ), ), ), ), + ); + }, + ), + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + if (_selectedEmoji != null) { + setState(() { + _selectedEmoji = null; + }); + return; + } + if (isDesktop) { + showDialog( + barrierColor: Colors.transparent, + context: context, + builder: (context) { + return const DesktopDialog( + maxHeight: 700, + maxWidth: 600, + child: Padding( + padding: EdgeInsets.only( + left: 32, + right: 20, + top: 32, + bottom: 32, + ), + child: EmojiSelectSheet(), + ), + ); + }).then((value) { + if (value is Emoji) { + setState(() { + _selectedEmoji = value; + }); + } + }); + } else { + showModalBottomSheet( + backgroundColor: Colors.transparent, + context: context, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + builder: (_) => const EmojiSelectSheet(), + ).then((value) { + if (value is Emoji) { + setState(() { + _selectedEmoji = value; + }); + } + }); + } + }, + child: SizedBox( + height: emojiSize, + width: emojiSize, + child: Stack( + children: [ + Container( + height: emojiSize, + width: emojiSize, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(emojiSize / 2), + color: Theme.of(context) + .extension()! + .textFieldActiveBG, + ), + child: Center( + child: _selectedEmoji == null + ? SvgPicture.asset( + Assets.svg.user, + height: emojiSize / 2, + width: emojiSize / 2, + ) + : Text( + _selectedEmoji!.char, + style: isDesktop + ? STextStyles.desktopH3(context) + : STextStyles.pageTitleH1(context), + ), + ), + ), + Align( + alignment: Alignment.bottomRight, + child: Container( + height: 14, + width: 14, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + color: Theme.of(context) + .extension()! + .accentColorDark), + child: Center( + child: _selectedEmoji == null + ? SvgPicture.asset( + Assets.svg.plus, + color: Theme.of(context) + .extension()! + .textWhite, + width: 12, + height: 12, + ) + : SvgPicture.asset( + Assets.svg.thickX, + color: Theme.of(context) + .extension()! + .textWhite, + width: 8, + height: 8, + ), + ), + ), + ) + ], + ), + ), + ), + if (isDesktop) + const SizedBox( + width: 8, + ), + if (isDesktop) + Expanded( + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + controller: nameController, + focusNode: nameFocusNode, + style: STextStyles.field(context), + onChanged: (_) => setState(() {}), + decoration: standardInputDecoration( + "Enter contact name", + nameFocusNode, + context, + ).copyWith( + suffixIcon: nameController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + nameController.text = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + ), + ], + ), + if (!isDesktop) + const SizedBox( + height: 8, ), - ); - }, + if (!isDesktop) + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + controller: nameController, + focusNode: nameFocusNode, + style: STextStyles.field(context), + onChanged: (_) => setState(() {}), + decoration: standardInputDecoration( + "Enter contact name", + nameFocusNode, + context, + ).copyWith( + suffixIcon: nameController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + nameController.text = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + const Spacer(), + const SizedBox( + height: 16, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Cancel", + buttonHeight: isDesktop ? ButtonHeight.l : null, + onPressed: () async { + if (!isDesktop && FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75)); + } + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + label: "Save", + enabled: nameController.text.isNotEmpty, + buttonHeight: isDesktop ? ButtonHeight.l : null, + onPressed: () async { + if (!isDesktop && FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75), + ); + } + final editedContact = contact.copyWith( + shouldCopyEmojiWithNull: true, + name: nameController.text, + emojiChar: + _selectedEmoji == null ? null : _selectedEmoji!.char, + ); + unawaited( + ref.read(addressBookServiceProvider).editContact( + editedContact, + ), + ); + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + ), + ], + ) + ], ), ); } diff --git a/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart b/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart index 88bd6abf0..690d1be98 100644 --- a/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart +++ b/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; @@ -28,6 +29,147 @@ class _DesktopContactOptionsMenuPopupState bool hoveredOnPencil = false; bool hoveredOnTrash = false; + void editContact() { + // pop context menu + Navigator.of(context).pop(); + + showDialog( + context: context, + useSafeArea: true, + barrierDismissible: true, + builder: (_) => DesktopDialog( + maxWidth: 580, + maxHeight: 400, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 32, + ), + child: Text( + "Edit contact", + style: STextStyles.desktopH3(context), + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only( + top: 16, + left: 32, + right: 32, + bottom: 32, + ), + child: EditContactNameEmojiView( + contactId: widget.contactId, + ), + ), + ), + ], + ), + ), + ); + } + + void attemptDeleteContact() { + 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: 400, + 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, + ); + }, + ), + ), + ), + ], + ) + ], + ), + ), + ), + ], + ), + ), + ); + } + @override Widget build(BuildContext context) { return Stack( @@ -148,9 +290,7 @@ class _DesktopContactOptionsMenuPopupState 1000, ), ), - onPressed: () { - print("should go to edit"); - }, + onPressed: editContact, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 25, @@ -213,111 +353,7 @@ class _DesktopContactOptionsMenuPopupState 1000, ), ), - 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, - ); - }, - ), - ), - ), - ], - ) - ], - ), - ), - ), - ], - ), - ), - ); - }, + onPressed: attemptDeleteContact, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 25,