add token functionality to desktop eth wallet view

This commit is contained in:
julian 2023-04-07 15:08:06 -06:00
parent 6beb4ec859
commit 6754c2ebdf
5 changed files with 276 additions and 276 deletions

View file

@ -13,7 +13,7 @@ class AddTokenList extends StatelessWidget {
final String walletId; final String walletId;
final List<AddTokenListElementData> items; final List<AddTokenListElementData> items;
final VoidCallback addFunction; final VoidCallback? addFunction;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -23,13 +23,13 @@ class AddTokenList extends StatelessWidget {
itemCount: items.length, itemCount: items.length,
itemBuilder: (ctx, index) { itemBuilder: (ctx, index) {
return ConditionalParent( return ConditionalParent(
condition: index == items.length - 1, condition: index == items.length - 1 && addFunction != null,
builder: (child) => Column( builder: (child) => Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
child, child,
AddCustomTokenSelector( AddCustomTokenSelector(
addFunction: addFunction, addFunction: addFunction!,
), ),
], ],
), ),

View file

@ -12,10 +12,9 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart';
@ -34,6 +33,8 @@ class MyTokensView extends ConsumerStatefulWidget {
} }
class _MyTokensViewState extends ConsumerState<MyTokensView> { class _MyTokensViewState extends ConsumerState<MyTokensView> {
final bool isDesktop = Util.isDesktop;
late final String walletAddress; late final String walletAddress;
late final TextEditingController _searchController; late final TextEditingController _searchController;
final searchFieldFocusNode = FocusNode(); final searchFieldFocusNode = FocusNode();
@ -56,222 +57,180 @@ class _MyTokensViewState extends ConsumerState<MyTokensView> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); debugPrint("BUILD: $runtimeType");
final isDesktop = Util.isDesktop; return ConditionalParent(
condition: !isDesktop,
return MasterScaffold( builder: (child) => Background(
background: Theme.of(context).extension<StackColors>()!.background, child: Scaffold(
isDesktop: isDesktop, backgroundColor:
appBar: isDesktop Theme.of(context).extension<StackColors>()!.background,
? DesktopAppBar( appBar: AppBar(
isCompactHeight: true, backgroundColor:
background: Theme.of(context).extension<StackColors>()!.popupBG, Theme.of(context).extension<StackColors>()!.background,
leading: Row( leading: AppBarBackButton(
children: [ onPressed: () async {
const SizedBox( if (FocusScope.of(context).hasFocus) {
width: 32, FocusScope.of(context).unfocus();
), await Future<void>.delayed(const Duration(milliseconds: 75));
AppBarIconButton( }
size: 32, if (mounted) {
color: Theme.of(context) Navigator.of(context).pop();
.extension<StackColors>()! }
.textFieldDefaultBG, },
),
title: Text(
"${ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(widget.walletId).walletName),
)} Tokens",
style: STextStyles.navBarTitle(context),
),
actions: [
Padding(
padding: const EdgeInsets.only(
top: 10,
bottom: 10,
right: 20,
),
child: AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
key: const Key("addTokenAppBarIconButtonKey"),
size: 36,
shadows: const [], shadows: const [],
color:
Theme.of(context).extension<StackColors>()!.background,
icon: SvgPicture.asset( icon: SvgPicture.asset(
Assets.svg.arrowLeft, Assets.svg.circlePlusDark,
width: 18,
height: 18,
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.topNavIconPrimary, .accentColorDark,
width: 20,
height: 20,
), ),
onPressed: Navigator.of(context).pop, onPressed: () async {
), final result = await Navigator.of(context).pushNamed(
const SizedBox( EditWalletTokensView.routeName,
width: 12, arguments: widget.walletId,
), );
Text(
"${ref.watch(
walletsChangeNotifierProvider.select((value) =>
value.getManager(widget.walletId).walletName),
)} Tokens",
style: STextStyles.desktopH3(context),
),
],
),
)
: AppBar(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
title: Text(
"${ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(widget.walletId).walletName),
)} Tokens",
style: STextStyles.navBarTitle(context),
),
actions: [
Padding(
padding: const EdgeInsets.only(
top: 10,
bottom: 10,
right: 20,
),
child: AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
key: const Key("addTokenAppBarIconButtonKey"),
size: 36,
shadows: const [],
color: Theme.of(context)
.extension<StackColors>()!
.background,
icon: SvgPicture.asset(
Assets.svg.circlePlusDark,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
width: 20,
height: 20,
),
onPressed: () async {
final result = await Navigator.of(context).pushNamed(
EditWalletTokensView.routeName,
arguments: widget.walletId,
);
if (mounted && result == 42) { if (mounted && result == 42) {
setState(() {}); setState(() {});
} }
}, },
),
),
),
],
),
body: Padding(
padding: const EdgeInsets.only(
left: 12,
top: 12,
right: 12,
),
child: child,
),
),
),
child: Column(
children: [
Padding(
padding: EdgeInsets.all(isDesktop ? 0 : 4),
child: Row(
children: [
ConditionalParent(
condition: isDesktop,
builder: (child) => Expanded(
child: child,
),
child: ConditionalParent(
condition: !isDesktop,
builder: (child) => Expanded(
child: child,
),
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<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,
),
),
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,
),
),
), ),
), ),
), ),
], ],
), ),
body: Padding( ),
padding: EdgeInsets.only( const SizedBox(
left: isDesktop ? 20 : 12, height: 8,
top: isDesktop ? 20 : 12, ),
right: isDesktop ? 20 : 12, Expanded(
), child: MyTokensList(
child: Column( walletId: widget.walletId,
children: [ searchTerm: _searchString,
Padding( tokenContracts: ref
padding: const EdgeInsets.all(4), .watch(walletsChangeNotifierProvider.select((value) => value
child: Row( .getManager(widget.walletId)
children: [ .wallet as EthereumWallet))
ConditionalParent( .getWalletTokenContractAddresses(),
condition: isDesktop,
builder: (child) => SizedBox(
width: 570,
child: child,
),
child: ConditionalParent(
condition: !isDesktop,
builder: (child) => Expanded(
child: child,
),
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<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,
),
),
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,
),
),
),
),
),
if (isDesktop)
const SizedBox(
width: 20,
),
// const NoTransActionsFound(),
],
),
), ),
const SizedBox( ),
height: 8, ],
),
Expanded(
child: MyTokensList(
walletId: widget.walletId,
searchTerm: _searchString,
tokenContracts: ref
.watch(walletsChangeNotifierProvider.select((value) => value
.getManager(widget.walletId)
.wallet as EthereumWallet))
.getWalletTokenContractAddresses(),
),
),
],
),
), ),
); );
} }

View file

@ -1,19 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart'; import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/pages/token_view/token_view.dart'; import 'package:stackwallet/pages/token_view/token_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart'; import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart'; import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/services/ethereum/cached_eth_token_balance.dart'; import 'package:stackwallet/services/ethereum/cached_eth_token_balance.dart';
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart'; import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/show_loading.dart'; import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
class MyTokenSelectItem extends ConsumerStatefulWidget { class MyTokenSelectItem extends ConsumerStatefulWidget {
@ -31,8 +31,38 @@ class MyTokenSelectItem extends ConsumerStatefulWidget {
} }
class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> { class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
final bool isDesktop = Util.isDesktop;
late final CachedEthTokenBalance cachedBalance; late final CachedEthTokenBalance cachedBalance;
void _onPressed() async {
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
token: widget.token,
secureStore: ref.read(secureStoreProvider),
ethWallet: ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as EthereumWallet,
tracker: TransactionNotificationTracker(
walletId: widget.walletId,
),
);
await showLoading<void>(
whileFuture: ref.read(tokenServiceProvider)!.initialize(),
context: context,
isDesktop: isDesktop,
message: "Loading ${widget.token.name}",
);
if (mounted) {
await Navigator.of(context).pushNamed(
isDesktop ? DesktopTokenView.routeName : TokenView.routeName,
arguments: widget.walletId,
);
}
}
@override @override
void initState() { void initState() {
cachedBalance = CachedEthTokenBalance(widget.walletId, widget.token); cachedBalance = CachedEthTokenBalance(widget.walletId, widget.token);
@ -57,47 +87,23 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
child: MaterialButton( child: MaterialButton(
key: Key("walletListItemButtonKey_${widget.token.symbol}"), key: Key("walletListItemButtonKey_${widget.token.symbol}"),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 13), padding: isDesktop
? const EdgeInsets.symmetric(horizontal: 28, vertical: 24)
: const EdgeInsets.symmetric(horizontal: 12, vertical: 13),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: borderRadius:
BorderRadius.circular(Constants.size.circularBorderRadius), BorderRadius.circular(Constants.size.circularBorderRadius),
), ),
onPressed: () async { onPressed: _onPressed,
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
token: widget.token,
secureStore: ref.read(secureStoreProvider),
ethWallet: ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as EthereumWallet,
tracker: TransactionNotificationTracker(
walletId: widget.walletId,
),
);
await showLoading<void>(
whileFuture: ref.read(tokenServiceProvider)!.initialize(),
context: context,
message: "Loading ${widget.token.name}",
);
if (mounted) {
await Navigator.of(context).pushNamed(
TokenView.routeName,
arguments: widget.walletId,
);
}
},
child: Row( child: Row(
children: [ children: [
SvgPicture.asset( EthTokenIcon(
Assets.svg.iconFor(coin: Coin.ethereum), contractAddress: widget.token.address,
width: 28, size: isDesktop ? 32 : 28,
height: 28,
), ),
const SizedBox( SizedBox(
width: 10, width: isDesktop ? 12 : 10,
), ),
Expanded( Expanded(
child: Consumer( child: Consumer(
@ -109,7 +115,9 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
children: [ children: [
Text( Text(
widget.token.name, widget.token.name,
style: STextStyles.titleBold12(context), style: isDesktop
? STextStyles.desktopTextExtraSmall(context)
: STextStyles.titleBold12(context),
), ),
const Spacer(), const Spacer(),
Text( Text(
@ -121,33 +129,44 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
), ),
)} " )} "
"${widget.token.symbol}", "${widget.token.symbol}",
style: STextStyles.itemSubtitle(context), style: isDesktop
? STextStyles.desktopTextExtraSmall(context)
: STextStyles.itemSubtitle(context),
), ),
], ],
), ),
const SizedBox( const SizedBox(
height: 1, height: 2,
), ),
Row( Row(
children: [ children: [
Text( Text(
widget.token.symbol, widget.token.symbol,
style: STextStyles.itemSubtitle(context), style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(
context)
: STextStyles.itemSubtitle(context),
), ),
const Spacer(), const Spacer(),
Text("${ref.watch( Text(
priceAnd24hChangeNotifierProvider.select( "${ref.watch(
(value) => value priceAnd24hChangeNotifierProvider.select(
.getTokenPrice(widget.token.address) (value) => value
.item1 .getTokenPrice(widget.token.address)
.toStringAsFixed(2), .item1
), .toStringAsFixed(2),
)} " ),
"${ref.watch( )} "
prefsChangeNotifierProvider.select( "${ref.watch(
(value) => value.currency, prefsChangeNotifierProvider.select(
), (value) => value.currency,
)}"), ),
)}",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(
context)
: STextStyles.itemSubtitle(context),
),
], ],
), ),
], ],

View file

@ -4,6 +4,7 @@ import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart'; import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/pages/token_view/sub_widgets/my_token_select_item.dart'; import 'package:stackwallet/pages/token_view/sub_widgets/my_token_select_item.dart';
import 'package:stackwallet/utilities/util.dart';
class MyTokensList extends StatelessWidget { class MyTokensList extends StatelessWidget {
const MyTokensList({ const MyTokensList({
@ -52,6 +53,8 @@ class MyTokensList extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final bool isDesktop = Util.isDesktop;
return Consumer( return Consumer(
builder: (_, ref, __) { builder: (_, ref, __) {
final tokens = _filter(searchTerm); final tokens = _filter(searchTerm);
@ -61,7 +64,9 @@ class MyTokensList extends StatelessWidget {
final token = tokens[index]; final token = tokens[index];
return Padding( return Padding(
key: Key(token.address), key: Key(token.address),
padding: const EdgeInsets.all(4), padding: isDesktop
? const EdgeInsets.symmetric(vertical: 5)
: const EdgeInsets.all(4),
child: MyTokenSelectItem( child: MyTokenSelectItem(
walletId: walletId, walletId: walletId,
token: token, token: token,

View file

@ -4,8 +4,9 @@ import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
import 'package:stackwallet/pages/token_view/my_tokens_view.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/transactions_list.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/transactions_list.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart';
@ -282,7 +283,7 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
"Recent transactions", "Tokens",
style: STextStyles.desktopTextExtraSmall(context) style: STextStyles.desktopTextExtraSmall(context)
.copyWith( .copyWith(
color: Theme.of(context) color: Theme.of(context)
@ -291,12 +292,20 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
), ),
), ),
CustomTextButton( CustomTextButton(
text: "See all", text: "Edit",
onTap: () { onTap: () async {
Navigator.of(context).pushNamed( final result = await showDialog<int?>(
AllTransactionsView.routeName, context: context,
arguments: widget.walletId, builder: (context) => EditWalletTokensView(
walletId: widget.walletId,
isDesktopPopup: true,
),
); );
if (result == 42) {
// wallet tokens were edited so update ui
setState(() {});
}
}, },
), ),
], ],
@ -321,12 +330,20 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
width: 16, width: 16,
), ),
Expanded( Expanded(
child: TransactionsList( child: ref.watch(walletsChangeNotifierProvider.select(
managerProvider: ref.watch( (value) => value
walletsChangeNotifierProvider.select((value) => .getManager(widget.walletId)
value.getManagerProvider(widget.walletId))), .hasTokenSupport))
walletId: widget.walletId, ? MyTokensView(
), walletId: widget.walletId,
)
: TransactionsList(
managerProvider: ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManagerProvider(
widget.walletId))),
walletId: widget.walletId,
),
), ),
], ],
), ),