diff --git a/lib/pages_desktop_specific/home/address_book_view/desktop_address_book.dart b/lib/pages_desktop_specific/home/address_book_view/desktop_address_book.dart index 51b075284..5e22a6089 100644 --- a/lib/pages_desktop_specific/home/address_book_view/desktop_address_book.dart +++ b/lib/pages_desktop_specific/home/address_book_view/desktop_address_book.dart @@ -5,7 +5,11 @@ import 'package:stackwallet/models/contact.dart'; import 'package:stackwallet/models/contact_address_entry.dart'; import 'package:stackwallet/pages/address_book_views/subviews/add_address_book_entry_view.dart'; import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart'; +import 'package:stackwallet/pages_desktop_specific/home/address_book_view/subwidgets/desktop_address_book_scaffold.dart'; +import 'package:stackwallet/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_details.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; +import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/providers/ui/address_book_providers/address_book_filter_provider.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -19,13 +23,11 @@ import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/rounded_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart'; -import '../../../providers/providers.dart'; -import '../../../providers/ui/address_book_providers/address_book_filter_provider.dart'; - class DesktopAddressBook extends ConsumerStatefulWidget { const DesktopAddressBook({Key? key}) : super(key: key); @@ -42,6 +44,8 @@ class _DesktopAddressBook extends ConsumerState { String _searchTerm = ""; + String? currentContactId; + Future selectCryptocurrency() async { await showDialog( context: context, @@ -139,8 +143,9 @@ class _DesktopAddressBook extends ConsumerState { .where((e) => ref.watch(addressBookFilterProvider .select((value) => value.coins.contains(e.coin)))) .isNotEmpty) - .where((e) => - ref.read(addressBookServiceProvider).matches(_searchTerm, e)); + .where( + (e) => ref.read(addressBookServiceProvider).matches(_searchTerm, e)) + .toList(); final favorites = contacts .where((element) => element.addresses @@ -150,7 +155,8 @@ class _DesktopAddressBook extends ConsumerState { .where((e) => e.isFavorite && ref.read(addressBookServiceProvider).matches(_searchTerm, e)) - .where((element) => element.isFavorite); + .where((element) => element.isFavorite) + .toList(); return DesktopScaffold( appBar: DesktopAppBar( @@ -294,12 +300,56 @@ class _DesktopAddressBook extends ConsumerState { padding: const EdgeInsets.all(0), child: Column( children: [ - ...favorites.map( - (e) => AddressBookCard( - key: Key("favContactCard_${e.id}_key"), - contactId: e.id, + for (int i = 0; i < favorites.length; i++) + Column( + children: [ + if (i > 0) + Container( + color: Theme.of(context) + .extension()! + .background, + height: 1, + ), + Padding( + padding: const EdgeInsets.all(4), + child: RoundedContainer( + padding: const EdgeInsets.all(0), + color: Theme.of(context) + .extension()! + .accentColorDark + .withOpacity( + currentContactId == favorites[i].id + ? 0.08 + : 0, + ), + child: RawMaterialButton( + onPressed: () { + setState(() { + currentContactId = favorites[i].id; + }); + }, + padding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 16, + ), + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: AddressBookCard( + key: Key( + "favContactCard_${favorites[i].id}_key"), + contactId: favorites[i].id, + desktopSendFrom: false, + ), + ), + ), + ), + ], ), - ), ], ), ), @@ -318,136 +368,70 @@ class _DesktopAddressBook extends ConsumerState { children: [ RoundedWhiteContainer( padding: const EdgeInsets.all(0), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - ...allContacts.map( - (e) => AddressBookCard( - key: Key("desktopContactCard_${e.id}_key"), - contactId: e.id, - ), + child: Column( + children: [ + for (int i = 0; i < allContacts.length; i++) + Column( + children: [ + if (i > 0) + Container( + color: Theme.of(context) + .extension()! + .background, + height: 1, + ), + Padding( + padding: const EdgeInsets.all(4), + child: RoundedContainer( + padding: const EdgeInsets.all(0), + color: Theme.of(context) + .extension()! + .accentColorDark + .withOpacity( + currentContactId == allContacts[i].id + ? 0.08 + : 0, + ), + child: RawMaterialButton( + onPressed: () { + setState(() { + currentContactId = allContacts[i].id; + }); + }, + padding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 16, + ), + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: AddressBookCard( + key: Key( + "favContactCard_${allContacts[i].id}_key"), + contactId: allContacts[i].id, + desktopSendFrom: false, + ), + ), + ), + ), + ], ), - ], - ), + ], ), ), ], ), - details: Container( - color: Colors.purple, - ), + details: currentContactId == null + ? Container() + : DesktopContactDetails( + contactId: currentContactId!, + ), ), ), ); } } - -class DesktopAddressBookScaffold extends StatelessWidget { - const DesktopAddressBookScaffold({ - Key? key, - required this.controlsLeft, - required this.controlsRight, - required this.filterItems, - required this.upperLabel, - required this.lowerLabel, - required this.favorites, - required this.all, - required this.details, - }) : super(key: key); - - final Widget? controlsLeft; - final Widget? controlsRight; - final Widget? filterItems; - final Widget? upperLabel; - final Widget? lowerLabel; - final Widget? favorites; - final Widget? all; - final Widget? details; - - static const double weirdRowHeight = 30; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Expanded( - flex: 6, - child: controlsLeft ?? Container(), - ), - const SizedBox( - width: 20, - ), - Expanded( - flex: 5, - child: controlsRight ?? Container(), - ), - ], - ), - const SizedBox( - height: 20, - ), - Row( - children: [ - Expanded( - child: filterItems ?? Container(), - ), - ], - ), - Expanded( - child: Row( - children: [ - Expanded( - flex: 6, - child: LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight, - ), - child: IntrinsicHeight( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - height: weirdRowHeight, - child: upperLabel, - ), - favorites ?? Container(), - lowerLabel ?? Container(), - all ?? Container(), - ], - ), - ), - ), - ); - }, - ), - ), - const SizedBox( - width: 20, - ), - Expanded( - flex: 5, - child: Column( - children: [ - const SizedBox( - height: weirdRowHeight, - ), - Expanded( - child: details ?? Container(), - ), - ], - ), - ), - ], - ), - ) - ], - ); - } -} diff --git a/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_address_book_scaffold.dart b/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_address_book_scaffold.dart new file mode 100644 index 000000000..f32ea1f7f --- /dev/null +++ b/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_address_book_scaffold.dart @@ -0,0 +1,111 @@ +import 'package:flutter/widgets.dart'; + +class DesktopAddressBookScaffold extends StatelessWidget { + const DesktopAddressBookScaffold({ + Key? key, + required this.controlsLeft, + required this.controlsRight, + required this.filterItems, + required this.upperLabel, + required this.lowerLabel, + required this.favorites, + required this.all, + required this.details, + }) : super(key: key); + + final Widget? controlsLeft; + final Widget? controlsRight; + final Widget? filterItems; + final Widget? upperLabel; + final Widget? lowerLabel; + final Widget? favorites; + final Widget? all; + final Widget? details; + + static const double weirdRowHeight = 30; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Expanded( + flex: 6, + child: controlsLeft ?? Container(), + ), + const SizedBox( + width: 20, + ), + Expanded( + flex: 5, + child: controlsRight ?? Container(), + ), + ], + ), + const SizedBox( + height: 20, + ), + Row( + children: [ + Expanded( + child: filterItems ?? Container(), + ), + ], + ), + Expanded( + child: Row( + children: [ + Expanded( + flex: 6, + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + primary: false, + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: weirdRowHeight, + child: upperLabel, + ), + favorites ?? Container(), + lowerLabel ?? Container(), + all ?? Container(), + ], + ), + ), + ), + ); + }, + ), + ), + const SizedBox( + width: 20, + ), + Expanded( + flex: 5, + child: Column( + children: [ + const SizedBox( + height: weirdRowHeight, + ), + Expanded( + child: details ?? Container(), + ), + ], + ), + ), + ], + ), + ) + ], + ); + } +} 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 new file mode 100644 index 000000000..5184b2293 --- /dev/null +++ b/lib/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_details.dart @@ -0,0 +1,191 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/models/contact_address_entry.dart'; +import 'package:stackwallet/providers/global/address_book_service_provider.dart'; +import 'package:stackwallet/utilities/assets.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'; + +class DesktopContactDetails extends ConsumerStatefulWidget { + const DesktopContactDetails({ + Key? key, + required this.contactId, + }) : super(key: key); + + final String contactId; + + @override + ConsumerState createState() => + _DesktopContactDetailsState(); +} + +class _DesktopContactDetailsState extends ConsumerState { + @override + Widget build(BuildContext context) { + final contact = ref.watch(addressBookServiceProvider + .select((value) => value.getContactById(widget.contactId))); + + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + borderRadius: BorderRadius.circular(32), + ), + child: contact.id == "default" + ? Center( + child: SvgPicture.asset( + Assets.svg.stackIcon(context), + width: 20, + ), + ) + : contact.emojiChar != null + ? Center( + child: Text(contact.emojiChar!), + ) + : Center( + child: SvgPicture.asset( + Assets.svg.user, + width: 18, + ), + ), + ), + const SizedBox( + width: 16, + ), + Text( + contact.name, + style: STextStyles.desktopTextSmall(context), + ), + ], + ), + SecondaryButton( + label: "Options", + onPressed: () {}, + ), + ], + ), + const SizedBox( + height: 24, + ), + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Addresses", + style: STextStyles.desktopTextExtraExtraSmall( + context), + ), + BlueTextButton( + text: "Add new", + onTap: () {}, + ), + ], + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + ...contact.addresses + .map((e) => AddressCard(entry: e)), + ], + ) + ], + ), + ), + ), + ); + }, + ), + ), + ], + ); + } +} + +class AddressCard extends StatelessWidget { + const AddressCard({ + Key? key, + required this.entry, + }) : super(key: key); + + final ContactAddressEntry entry; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + SvgPicture.asset( + Assets.svg.iconFor( + coin: entry.coin, + ), + height: 32, + width: 32, + ), + const SizedBox( + width: 16, + ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SelectableText( + "${entry.label} ${entry.coin.ticker}", + style: STextStyles.desktopTextExtraExtraSmall(context).copyWith( + color: Theme.of(context).extension()!.textDark, + ), + ), + const SizedBox( + height: 2, + ), + SelectableText( + entry.address, + style: STextStyles.desktopTextExtraExtraSmall(context), + ), + const SizedBox( + height: 8, + ), + Row( + children: [ + BlueTextButton( + text: "Copy", + onTap: () {}, + ), + const SizedBox( + width: 16, + ), + BlueTextButton( + text: "Edit", + onTap: () {}, + ), + ], + ) + ], + ), + ], + ); + } +} diff --git a/lib/widgets/address_book_card.dart b/lib/widgets/address_book_card.dart index 329e35fdf..b79f89662 100644 --- a/lib/widgets/address_book_card.dart +++ b/lib/widgets/address_book_card.dart @@ -18,10 +18,12 @@ class AddressBookCard extends ConsumerStatefulWidget { Key? key, required this.contactId, this.indicatorDown, + this.desktopSendFrom = true, }) : super(key: key); final String contactId; final ExpandableState? indicatorDown; + final bool desktopSendFrom; @override ConsumerState createState() => _AddressBookCardState(); @@ -30,10 +32,12 @@ class AddressBookCard extends ConsumerStatefulWidget { class _AddressBookCardState extends ConsumerState { late final String contactId; late final bool isDesktop; + late final bool desktopSendFrom; @override void initState() { contactId = widget.contactId; + desktopSendFrom = widget.desktopSendFrom; isDesktop = Util.isDesktop; super.initState(); } @@ -107,6 +111,7 @@ class _AddressBookCardState extends ConsumerState { const SizedBox( width: 16, ), + if (isDesktop && !desktopSendFrom) const Spacer(), if (isDesktop) Text( coinsString, @@ -129,8 +134,8 @@ class _AddressBookCardState extends ConsumerState { ), ], ), - if (isDesktop) const Spacer(), - if (isDesktop) + if (isDesktop && desktopSendFrom) const Spacer(), + if (isDesktop && desktopSendFrom) SvgPicture.asset( widget.indicatorDown == ExpandableState.collapsed ? Assets.svg.chevronDown