From 0e4b664e63f57228ce1ffd2cdcdf0a2276e0b4c7 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 28 Feb 2023 16:03:56 -0600 Subject: [PATCH] WIP custom token addition --- .../add_token_view/add_custom_token_view.dart | 189 ++++++++++++++++++ .../add_token_view/add_token_view.dart | 14 +- .../sub_widgets/add_token_list.dart | 12 +- lib/route_generator.dart | 15 ++ 4 files changed, 224 insertions(+), 6 deletions(-) create mode 100644 lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart diff --git a/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart b/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart new file mode 100644 index 000000000..f10c11d41 --- /dev/null +++ b/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart @@ -0,0 +1,189 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/models/ethereum/eth_token.dart'; +import 'package:stackwallet/services/ethereum/ethereum_api.dart'; +import 'package:stackwallet/utilities/show_loading.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/background.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; + +class AddCustomTokenView extends ConsumerStatefulWidget { + const AddCustomTokenView({ + Key? key, + required this.walletId, + }) : super(key: key); + + final String walletId; + + static const routeName = "/addCustomToken"; + + @override + ConsumerState createState() => _AddCustomTokenViewState(); +} + +class _AddCustomTokenViewState extends ConsumerState { + final isDesktop = Util.isDesktop; + + final contractController = TextEditingController(); + final nameController = TextEditingController(); + final symbolController = TextEditingController(); + final decimalsController = TextEditingController(); + + bool enableSubFields = false; + bool addTokenButtonEnabled = false; + + EthToken? currentToken; + + @override + Widget build(BuildContext context) { + return Background( + child: Scaffold( + backgroundColor: Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + body: Padding( + padding: const EdgeInsets.only( + top: 10, + left: 16, + right: 16, + bottom: 16, + ), + child: Column( + children: [ + Text( + "Add custom ETH token", + style: STextStyles.pageTitleH1(context), + ), + const SizedBox( + height: 16, + ), + Text("Add custom ETH token"), + const SizedBox( + height: 16, + ), + TextField( + autocorrect: !isDesktop, + enableSuggestions: !isDesktop, + controller: contractController, + style: STextStyles.field(context), + decoration: InputDecoration( + hintText: "Contract address", + hintStyle: STextStyles.fieldLabel(context), + ), + ), + const SizedBox( + height: 8, + ), + PrimaryButton( + label: "Search", + onPressed: () async { + final response = await showLoading( + whileFuture: EthereumAPI.getTokenByContractAddress( + contractController.text), + context: context, + message: "Looking up contract", + ); + currentToken = response.value; + if (currentToken != null) { + nameController.text = currentToken!.name; + symbolController.text = currentToken!.symbol; + decimalsController.text = currentToken!.decimals.toString(); + } else { + nameController.text = ""; + symbolController.text = ""; + decimalsController.text = ""; + unawaited( + showDialog( + context: context, + builder: (context) => StackOkDialog( + title: "Failed to look up token", + message: response.exception?.message, + ), + ), + ); + } + setState(() { + addTokenButtonEnabled = currentToken != null; + }); + }, + ), + const SizedBox( + height: 8, + ), + TextField( + enabled: enableSubFields, + autocorrect: !isDesktop, + enableSuggestions: !isDesktop, + controller: nameController, + style: STextStyles.field(context), + decoration: InputDecoration( + hintText: "Token name", + hintStyle: STextStyles.fieldLabel(context), + ), + ), + const SizedBox( + height: 8, + ), + TextField( + enabled: enableSubFields, + autocorrect: !isDesktop, + enableSuggestions: !isDesktop, + controller: symbolController, + style: STextStyles.field(context), + decoration: InputDecoration( + hintText: "Ticker", + hintStyle: STextStyles.fieldLabel(context), + ), + ), + const SizedBox( + height: 8, + ), + TextField( + enabled: enableSubFields, + autocorrect: !isDesktop, + enableSuggestions: !isDesktop, + controller: decimalsController, + style: STextStyles.field(context), + inputFormatters: [ + TextInputFormatter.withFunction((oldValue, newValue) => + RegExp(r'^([0-9]*)$').hasMatch(newValue.text) + ? newValue + : oldValue), + ], + keyboardType: const TextInputType.numberWithOptions( + signed: false, + decimal: false, + ), + decoration: InputDecoration( + hintText: "Decimals", + hintStyle: STextStyles.fieldLabel(context), + ), + ), + const SizedBox( + height: 16, + ), + const Spacer(), + PrimaryButton( + label: "Add token", + enabled: addTokenButtonEnabled, + onPressed: () {}, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/add_wallet_views/add_token_view/add_token_view.dart b/lib/pages/add_wallet_views/add_token_view/add_token_view.dart index 131a37f82..1fa47ba4d 100644 --- a/lib/pages/add_wallet_views/add_token_view/add_token_view.dart +++ b/lib/pages/add_wallet_views/add_token_view/add_token_view.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/pages/add_wallet_views/add_token_view/add_custom_token_view.dart'; import 'package:stackwallet/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart'; import 'package:stackwallet/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list_element.dart'; import 'package:stackwallet/pages/add_wallet_views/add_token_view/sub_widgets/add_token_text.dart'; @@ -70,6 +71,13 @@ class _AddTokenViewState extends ConsumerState { print("SELECTED TOKENS: $selectedTokens"); } + void onAddCustomTokenPressed() { + Navigator.of(context).pushNamed( + AddCustomTokenView.routeName, + arguments: widget.walletId, + ); + } + @override void initState() { _searchFieldController = TextEditingController(); @@ -196,6 +204,7 @@ class _AddTokenViewState extends ConsumerState { ), Expanded( child: AddTokenList( + walletId: widget.walletId, items: filter(_searchTerm, tokenEntities), ), ), @@ -244,9 +253,7 @@ class _AddTokenViewState extends ConsumerState { .extension()! .topNavIconPrimary, ), - onPressed: () { - // todo add custom token - }, + onPressed: onAddCustomTokenPressed, ), ), ), @@ -323,6 +330,7 @@ class _AddTokenViewState extends ConsumerState { ), Expanded( child: AddTokenList( + walletId: widget.walletId, items: filter(_searchTerm, tokenEntities), ), ), diff --git a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart index 1fd0c13d2..56516eaf0 100644 --- a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart +++ b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/pages/add_wallet_views/add_token_view/add_custom_token_view.dart'; import 'package:stackwallet/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list_element.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; @@ -10,9 +11,11 @@ import 'package:stackwallet/widgets/conditional_parent.dart'; class AddTokenList extends StatelessWidget { const AddTokenList({ Key? key, + required this.walletId, required this.items, }) : super(key: key); + final String walletId; final List items; @override @@ -29,7 +32,7 @@ class AddTokenList extends StatelessWidget { children: [ child, Padding( - padding: const EdgeInsets.all(4), + padding: const EdgeInsets.symmetric(vertical: 4), child: RawMaterialButton( fillColor: Theme.of(context).extension()!.popupBG, @@ -44,7 +47,10 @@ class AddTokenList extends StatelessWidget { ), ), onPressed: () { - // todo add custom token + Navigator.of(context).pushNamed( + AddCustomTokenView.routeName, + arguments: walletId, + ); }, child: Padding( padding: const EdgeInsets.all(12), @@ -73,7 +79,7 @@ class AddTokenList extends StatelessWidget { ], ), child: Padding( - padding: const EdgeInsets.all(4), + padding: const EdgeInsets.symmetric(vertical: 4), child: AddTokenListElement( data: items[index], ), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 9f7dece46..01e6ab48f 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -12,6 +12,7 @@ import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart' import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/paynym/paynym_account_lite.dart'; import 'package:stackwallet/models/send_view_auto_fill_data.dart'; +import 'package:stackwallet/pages/add_wallet_views/add_token_view/add_custom_token_view.dart'; import 'package:stackwallet/pages/add_wallet_views/add_token_view/add_token_view.dart'; import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart'; import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart'; @@ -218,6 +219,20 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case AddCustomTokenView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => AddCustomTokenView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + case PaynymClaimView.routeName: if (args is String) { return getRoute(