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/isar/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/themes/stack_colors.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/text_styles.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/loading_indicator.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({ Key? key, required this.walletId, }) : super(key: key); static const String routeName = "/walletAddressesView"; final String walletId; @override ConsumerState createState() => _WalletAddressesViewState(); } class _WalletAddressesViewState extends ConsumerState { final bool isDesktop = Util.isDesktop; 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) { 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: Padding( padding: const EdgeInsets.all(16), child: child, ), ), ), child: Column( children: [ SizedBox( width: isDesktop ? 490 : null, 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, ), ), ), ), SizedBox( height: isDesktop ? 20 : 16, ), Expanded( child: FutureBuilder( future: _search(_searchString), builder: (context, AsyncSnapshot> snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) { // listview return ListView.separated( itemCount: snapshot.data!.length, separatorBuilder: (_, __) => Container( height: 10, ), itemBuilder: (_, index) => AddressCard( walletId: widget.walletId, addressId: snapshot.data![index], coin: coin, onPressed: () { Navigator.of(context).pushNamed( AddressDetailsView.routeName, arguments: Tuple2( snapshot.data![index], widget.walletId, ), ); }, ), ); } else { return const Center( child: LoadingIndicator( height: 200, width: 200, ), ); } }, ), ), ], ), ); } }