2023-05-26 21:21:16 +00:00
|
|
|
/*
|
|
|
|
* This file is part of Stack Wallet.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2023 Cypher Stack
|
|
|
|
* All Rights Reserved.
|
|
|
|
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
|
|
|
* Generated by Cypher Stack on 2023-05-26
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2023-03-28 22:18:11 +00:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
2023-04-10 15:44:38 +00:00
|
|
|
import 'package:flutter_svg/svg.dart';
|
2023-05-04 20:52:58 +00:00
|
|
|
import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/coin_entity.dart';
|
2023-04-10 15:44:38 +00:00
|
|
|
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
|
2023-05-04 20:52:58 +00:00
|
|
|
import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart';
|
2023-04-10 15:44:38 +00:00
|
|
|
import 'package:stackwallet/pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart';
|
|
|
|
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
2023-03-28 22:18:11 +00:00
|
|
|
import 'package:stackwallet/providers/providers.dart';
|
2023-04-10 15:44:38 +00:00
|
|
|
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
|
|
|
|
import 'package:stackwallet/services/coins/manager.dart';
|
2023-05-09 21:57:40 +00:00
|
|
|
import 'package:stackwallet/themes/stack_colors.dart';
|
2023-04-10 15:44:38 +00:00
|
|
|
import 'package:stackwallet/utilities/assets.dart';
|
|
|
|
import 'package:stackwallet/utilities/constants.dart';
|
2023-03-28 22:18:11 +00:00
|
|
|
import 'package:stackwallet/utilities/enums/coin_enum.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';
|
2023-04-10 15:44:38 +00:00
|
|
|
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
2023-03-28 22:18:11 +00:00
|
|
|
import 'package:stackwallet/widgets/master_wallet_card.dart';
|
2023-04-10 15:44:38 +00:00
|
|
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
|
|
|
import 'package:stackwallet/widgets/stack_text_field.dart';
|
|
|
|
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
2023-04-10 15:12:07 +00:00
|
|
|
import 'package:stackwallet/widgets/wallet_card.dart';
|
2023-04-10 15:44:38 +00:00
|
|
|
import 'package:tuple/tuple.dart';
|
2023-03-28 22:18:11 +00:00
|
|
|
|
2023-04-10 15:12:07 +00:00
|
|
|
class WalletsOverview extends ConsumerStatefulWidget {
|
|
|
|
const WalletsOverview({
|
|
|
|
Key? key,
|
|
|
|
required this.coin,
|
2023-04-10 15:44:38 +00:00
|
|
|
this.navigatorState,
|
2023-04-10 15:12:07 +00:00
|
|
|
}) : super(key: key);
|
|
|
|
|
|
|
|
final Coin coin;
|
2023-04-10 15:44:38 +00:00
|
|
|
final NavigatorState? navigatorState;
|
2023-03-28 22:18:11 +00:00
|
|
|
|
2023-04-10 15:44:38 +00:00
|
|
|
static const routeName = "/walletsOverview";
|
2023-03-28 22:18:11 +00:00
|
|
|
|
|
|
|
@override
|
2023-04-10 15:12:07 +00:00
|
|
|
ConsumerState<WalletsOverview> createState() => _EthWalletsOverviewState();
|
2023-03-28 22:18:11 +00:00
|
|
|
}
|
|
|
|
|
2023-04-10 15:12:07 +00:00
|
|
|
class _EthWalletsOverviewState extends ConsumerState<WalletsOverview> {
|
2023-03-28 22:18:11 +00:00
|
|
|
final isDesktop = Util.isDesktop;
|
|
|
|
|
2023-04-10 15:44:38 +00:00
|
|
|
late final TextEditingController _searchController;
|
|
|
|
late final FocusNode searchFieldFocusNode;
|
|
|
|
|
|
|
|
String _searchString = "";
|
|
|
|
|
|
|
|
final List<Tuple2<Manager, List<EthContract>>> wallets = [];
|
|
|
|
|
|
|
|
List<Tuple2<Manager, List<EthContract>>> _filter(String searchTerm) {
|
|
|
|
if (searchTerm.isEmpty) {
|
|
|
|
return wallets;
|
|
|
|
}
|
|
|
|
|
|
|
|
final List<Tuple2<Manager, List<EthContract>>> results = [];
|
|
|
|
final term = searchTerm.toLowerCase();
|
|
|
|
|
|
|
|
for (final tuple in wallets) {
|
|
|
|
bool includeManager = false;
|
|
|
|
// search wallet name and total balance
|
|
|
|
includeManager |= _elementContains(tuple.item1.walletName, term);
|
|
|
|
includeManager |= _elementContains(
|
|
|
|
tuple.item1.balance.total.decimal.toString(),
|
|
|
|
term,
|
|
|
|
);
|
|
|
|
|
|
|
|
final List<EthContract> contracts = [];
|
|
|
|
|
|
|
|
for (final contract in tuple.item2) {
|
|
|
|
if (_elementContains(contract.name, term)) {
|
|
|
|
contracts.add(contract);
|
|
|
|
} else if (_elementContains(contract.symbol, term)) {
|
|
|
|
contracts.add(contract);
|
|
|
|
} else if (_elementContains(contract.type.name, term)) {
|
|
|
|
contracts.add(contract);
|
|
|
|
} else if (_elementContains(contract.address, term)) {
|
|
|
|
contracts.add(contract);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (includeManager || contracts.isNotEmpty) {
|
|
|
|
results.add(Tuple2(tuple.item1, contracts));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool _elementContains(String element, String term) {
|
|
|
|
return element.toLowerCase().contains(term);
|
|
|
|
}
|
2023-03-28 22:18:11 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
2023-04-10 15:44:38 +00:00
|
|
|
_searchController = TextEditingController();
|
|
|
|
searchFieldFocusNode = FocusNode();
|
|
|
|
|
2023-03-28 22:18:11 +00:00
|
|
|
final walletsData =
|
|
|
|
ref.read(walletsServiceChangeNotifierProvider).fetchWalletsData();
|
2023-04-10 15:12:07 +00:00
|
|
|
walletsData.removeWhere((key, value) => value.coin != widget.coin);
|
2023-03-28 22:18:11 +00:00
|
|
|
|
2023-04-10 15:44:38 +00:00
|
|
|
if (widget.coin == Coin.ethereum) {
|
|
|
|
for (final data in walletsData.values) {
|
|
|
|
final List<EthContract> contracts = [];
|
|
|
|
final manager =
|
|
|
|
ref.read(walletsChangeNotifierProvider).getManager(data.walletId);
|
|
|
|
final contractAddresses = (manager.wallet as EthereumWallet)
|
|
|
|
.getWalletTokenContractAddresses();
|
|
|
|
|
|
|
|
// fetch each contract
|
|
|
|
for (final contractAddress in contractAddresses) {
|
|
|
|
final contract = ref
|
|
|
|
.read(
|
|
|
|
mainDBProvider,
|
|
|
|
)
|
|
|
|
.getEthContractSync(
|
|
|
|
contractAddress,
|
|
|
|
);
|
|
|
|
|
|
|
|
// add it to list if it exists in DB
|
|
|
|
if (contract != null) {
|
|
|
|
contracts.add(contract);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add tuple to list
|
|
|
|
wallets.add(
|
|
|
|
Tuple2(
|
|
|
|
ref.read(walletsChangeNotifierProvider).getManager(
|
|
|
|
data.walletId,
|
|
|
|
),
|
|
|
|
contracts,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// add non token wallet tuple to list
|
|
|
|
for (final data in walletsData.values) {
|
|
|
|
wallets.add(
|
|
|
|
Tuple2(
|
|
|
|
ref.read(walletsChangeNotifierProvider).getManager(
|
|
|
|
data.walletId,
|
|
|
|
),
|
|
|
|
[],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2023-03-28 22:18:11 +00:00
|
|
|
|
|
|
|
super.initState();
|
|
|
|
}
|
|
|
|
|
2023-04-10 15:44:38 +00:00
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
_searchController.dispose();
|
|
|
|
searchFieldFocusNode.dispose();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
2023-03-28 22:18:11 +00:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2023-04-10 22:12:14 +00:00
|
|
|
return ConditionalParent(
|
|
|
|
condition: !isDesktop,
|
|
|
|
builder: (child) => Background(
|
|
|
|
child: Scaffold(
|
2023-03-28 22:18:11 +00:00
|
|
|
backgroundColor:
|
|
|
|
Theme.of(context).extension<StackColors>()!.background,
|
|
|
|
appBar: AppBar(
|
|
|
|
leading: const AppBarBackButton(),
|
|
|
|
title: Text(
|
2023-04-10 15:12:07 +00:00
|
|
|
"${widget.coin.prettyName} (${widget.coin.ticker}) wallets",
|
2023-03-28 22:18:11 +00:00
|
|
|
style: STextStyles.navBarTitle(context),
|
|
|
|
),
|
2023-05-04 20:52:58 +00:00
|
|
|
actions: [
|
|
|
|
AspectRatio(
|
|
|
|
aspectRatio: 1,
|
|
|
|
child: AppBarIconButton(
|
|
|
|
icon: SvgPicture.asset(
|
|
|
|
Assets.svg.plus,
|
|
|
|
width: 18,
|
|
|
|
height: 18,
|
|
|
|
color: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.topNavIconPrimary,
|
|
|
|
),
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pushNamed(
|
|
|
|
CreateOrRestoreWalletView.routeName,
|
|
|
|
arguments: CoinEntity(widget.coin),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
2023-03-28 22:18:11 +00:00
|
|
|
),
|
|
|
|
body: SafeArea(
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.all(16),
|
|
|
|
child: child,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
2023-04-10 22:12:14 +00:00
|
|
|
),
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
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<StackColors>()!
|
|
|
|
.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,
|
2023-04-10 15:44:38 +00:00
|
|
|
),
|
2023-04-10 15:12:07 +00:00
|
|
|
),
|
2023-04-10 22:12:14 +00:00
|
|
|
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,
|
2023-04-10 15:44:38 +00:00
|
|
|
),
|
|
|
|
),
|
2023-04-10 22:12:14 +00:00
|
|
|
),
|
|
|
|
const SizedBox(
|
|
|
|
height: 16,
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Builder(
|
|
|
|
builder: (context) {
|
|
|
|
final data = _filter(_searchString);
|
|
|
|
return ListView.separated(
|
|
|
|
itemBuilder: (_, index) {
|
|
|
|
final element = data[index];
|
2023-04-10 15:44:38 +00:00
|
|
|
|
2023-04-10 22:12:14 +00:00
|
|
|
if (element.item1.hasTokenSupport) {
|
|
|
|
if (isDesktop) {
|
|
|
|
return DesktopExpandingWalletCard(
|
|
|
|
key: Key(
|
|
|
|
"${element.item1.walletName}_${element.item2.map((e) => e.address).join()}"),
|
|
|
|
data: element,
|
|
|
|
navigatorState: widget.navigatorState!,
|
|
|
|
);
|
2023-04-10 15:44:38 +00:00
|
|
|
} else {
|
2023-04-10 22:12:14 +00:00
|
|
|
return MasterWalletCard(
|
|
|
|
walletId: element.item1.walletId,
|
2023-04-10 15:44:38 +00:00
|
|
|
);
|
|
|
|
}
|
2023-04-10 22:12:14 +00:00
|
|
|
} else {
|
|
|
|
return ConditionalParent(
|
|
|
|
condition: isDesktop,
|
|
|
|
builder: (child) => RoundedWhiteContainer(
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
vertical: 14,
|
|
|
|
horizontal: 20,
|
|
|
|
),
|
|
|
|
borderColor: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.backgroundAppBar,
|
|
|
|
child: child,
|
|
|
|
),
|
|
|
|
child: SimpleWalletCard(
|
|
|
|
walletId: element.item1.walletId,
|
|
|
|
popPrevious: isDesktop,
|
|
|
|
desktopNavigatorState:
|
|
|
|
isDesktop ? widget.navigatorState : null,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
separatorBuilder: (_, __) => SizedBox(
|
|
|
|
height: isDesktop ? 10 : 8,
|
|
|
|
),
|
|
|
|
itemCount: data.length,
|
|
|
|
);
|
|
|
|
},
|
2023-04-10 15:44:38 +00:00
|
|
|
),
|
2023-04-10 22:12:14 +00:00
|
|
|
),
|
|
|
|
],
|
2023-03-28 22:18:11 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|