desktop edit/add token to eth wallet

This commit is contained in:
julian 2023-04-07 08:45:05 -06:00
parent 94896dfd60
commit cbe6a5caf8
8 changed files with 262 additions and 208 deletions

View file

@ -1,13 +1,17 @@
import 'dart:async';
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/ethereum/eth_contract.dart';
import 'package:stackwallet/notifications/show_flush_bar.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';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
@ -27,21 +31,24 @@ import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class AddTokenView extends ConsumerStatefulWidget {
const AddTokenView({
class EditWalletTokensView extends ConsumerStatefulWidget {
const EditWalletTokensView({
Key? key,
required this.walletId,
this.contractsToMarkSelected,
}) : super(key: key);
final String walletId;
final List<String>? contractsToMarkSelected;
static const routeName = "/addToken";
static const routeName = "/editWalletTokens";
@override
ConsumerState<AddTokenView> createState() => _AddTokenViewState();
ConsumerState<EditWalletTokensView> createState() =>
_EditWalletTokensViewState();
}
class _AddTokenViewState extends ConsumerState<AddTokenView> {
class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
late final TextEditingController _searchFieldController;
late final FocusNode _searchFocusNode;
@ -82,7 +89,20 @@ class _AddTokenViewState extends ConsumerState<AddTokenView> {
await ethWallet.updateTokenContracts(selectedTokens);
if (mounted) {
Navigator.of(context).pop(42);
if (widget.contractsToMarkSelected == null) {
Navigator.of(context).pop(42);
} else {
Navigator.of(context).popUntil(
ModalRoute.withName(DesktopHomeView.routeName),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "${ethWallet.walletName} tokens saved",
context: context,
),
);
}
}
}
@ -127,8 +147,13 @@ class _AddTokenViewState extends ConsumerState<AddTokenView> {
.wallet as EthereumWallet)
.getWalletTokenContractAddresses();
for (var e in tokenEntities) {
e.selected = walletContracts.contains(e.token.address);
final shouldMarkAsSelectedContracts = [
...walletContracts,
...(widget.contractsToMarkSelected ?? []),
];
for (final e in tokenEntities) {
e.selected = shouldMarkAsSelectedContracts.contains(e.token.address);
}
super.initState();
@ -149,10 +174,12 @@ class _AddTokenViewState extends ConsumerState<AddTokenView> {
if (isDesktop) {
return DesktopScaffold(
appBar: const DesktopAppBar(
appBar: DesktopAppBar(
isCompactHeight: false,
leading: AppBarBackButton(),
trailing: ExitToMyStackButton(),
leading: const AppBarBackButton(),
trailing: widget.contractsToMarkSelected == null
? const ExitToMyStackButton()
: null,
),
body: Column(
children: [
@ -169,83 +196,83 @@ class _AddTokenViewState extends ConsumerState<AddTokenView> {
child: RoundedWhiteContainer(
radiusMultiplier: 2,
padding: const EdgeInsets.only(
left: 16,
top: 16,
right: 16,
left: 20,
top: 20,
right: 20,
bottom: 0,
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(4.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _searchFieldController,
focusNode: _searchFocusNode,
onChanged: (value) {
setState(() {
_searchTerm = value;
});
},
style:
STextStyles.desktopTextMedium(context).copyWith(
height: 2,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _searchFieldController,
focusNode: _searchFocusNode,
onChanged: (value) {
setState(() {
_searchTerm = value;
});
},
style:
STextStyles.desktopTextMedium(context).copyWith(
height: 2,
decoration: standardInputDecoration(
"Search",
_searchFocusNode,
context,
).copyWith(
contentPadding: const EdgeInsets.symmetric(
vertical: 10,
),
decoration: standardInputDecoration(
"Search",
_searchFocusNode,
context,
).copyWith(
contentPadding: const EdgeInsets.symmetric(
vertical: 10,
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
// vertical: 20,
),
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
// vertical: 20,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 24,
height: 24,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultSearchIconLeft,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 24,
height: 24,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultSearchIconLeft,
),
suffixIcon: _searchFieldController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 10),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(
width: 24,
height: 24,
),
onTap: () async {
setState(() {
_searchFieldController.text =
"";
_searchTerm = "";
});
},
),
suffixIcon: _searchFieldController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 10),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(
width: 24,
height: 24,
),
],
),
onTap: () async {
setState(() {
_searchFieldController.text =
"";
_searchTerm = "";
});
},
),
],
),
)
: null,
),
),
)
: null,
),
),
),
const SizedBox(
height: 12,
),
Expanded(
child: AddTokenList(
walletId: widget.walletId,
@ -253,19 +280,22 @@ class _AddTokenViewState extends ConsumerState<AddTokenView> {
addFunction: _addToken,
),
),
const SizedBox(
height: 12,
),
],
),
),
),
),
const SizedBox(
height: 16,
height: 26,
),
SizedBox(
height: 70,
width: 480,
child: PrimaryButton(
label: "Next",
label: widget.contractsToMarkSelected != null ? "Save" : "Next",
onPressed: onNextPressed,
),
),
@ -384,7 +414,9 @@ class _AddTokenViewState extends ConsumerState<AddTokenView> {
height: 16,
),
PrimaryButton(
label: "Next",
label: widget.contractsToMarkSelected != null
? "Save"
: "Next",
onPressed: onNextPressed,
),
],

View file

@ -31,46 +31,6 @@ class AddTokenList extends StatelessWidget {
AddCustomTokenSelector(
addFunction: addFunction,
),
// Padding(
// padding: const EdgeInsets.symmetric(vertical: 4),
// child: RawMaterialButton(
// fillColor:
// Theme.of(context).extension<StackColors>()!.popupBG,
// elevation: 0,
// focusElevation: 0,
// hoverElevation: 0,
// highlightElevation: 0,
// constraints: const BoxConstraints(),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(
// Constants.size.circularBorderRadius,
// ),
// ),
// onPressed: addFunction,
// child: Padding(
// padding: const EdgeInsets.all(12),
// child: Row(
// children: [
// SvgPicture.asset(
// Assets.svg.circlePlusFilled,
// color: Theme.of(context)
// .extension<StackColors>()!
// .textDark,
// width: 24,
// height: 24,
// ),
// const SizedBox(
// width: 12,
// ),
// Text(
// "Add custom token",
// style: STextStyles.w600_14(context),
// ),
// ],
// ),
// ),
// ),
// ),
],
),
child: Padding(

View file

@ -7,6 +7,9 @@ import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dar
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
import 'package:stackwallet/utilities/assets.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/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -27,6 +30,8 @@ class AddTokenListElement extends StatefulWidget {
}
class _AddTokenListElementState extends State<AddTokenListElement> {
final bool isDesktop = Util.isDesktop;
@override
Widget build(BuildContext context) {
final currency = ExchangeDataLoadingService.instance.isar.currencies
@ -41,7 +46,14 @@ class _AddTokenListElementState extends State<AddTokenListElement> {
.imageIsNotEmpty()
.findFirstSync();
final String mainLabel = widget.data.token.name;
final double iconSize = isDesktop ? 32 : 24;
return RoundedWhiteContainer(
padding: EdgeInsets.all(isDesktop ? 16 : 12),
borderColor: isDesktop
? Theme.of(context).extension<StackColors>()!.backgroundAppBar
: null,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -50,39 +62,66 @@ class _AddTokenListElementState extends State<AddTokenListElement> {
currency != null
? SvgPicture.network(
currency.image,
width: 24,
height: 24,
width: iconSize,
height: iconSize,
)
: SvgPicture.asset(
widget.data.token.symbol == "BNB"
? Assets.svg.bnbIcon
: Assets.svg.ethereum,
width: 24,
height: 24,
width: iconSize,
height: iconSize,
),
const SizedBox(
width: 12,
),
Text(
"${widget.data.token.name} (${widget.data.token.symbol})",
style: STextStyles.w600_14(context),
overflow: TextOverflow.ellipsis,
ConditionalParent(
condition: isDesktop,
builder: (child) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
child,
const SizedBox(
height: 2,
),
Text(
widget.data.token.symbol,
style: STextStyles.desktopTextExtraExtraSmall(context),
overflow: TextOverflow.ellipsis,
),
],
),
child: Text(
isDesktop
? mainLabel
: "$mainLabel (${widget.data.token.symbol})",
style: isDesktop
? STextStyles.desktopTextSmall(context)
: STextStyles.w600_14(context),
overflow: TextOverflow.ellipsis,
),
),
],
),
const SizedBox(
width: 4,
),
SizedBox(
height: 20,
width: 40,
child: DraggableSwitchButton(
isOn: widget.data.selected,
onValueChanged: (newValue) {
widget.data.selected = newValue;
},
),
),
isDesktop
? Checkbox(
value: widget.data.selected,
onChanged: (newValue) =>
setState(() => widget.data.selected = newValue!),
)
: SizedBox(
height: 20,
width: 40,
child: DraggableSwitchButton(
isOn: widget.data.selected,
onValueChanged: (newValue) {
widget.data.selected = newValue;
},
),
),
],
),
);

View file

@ -12,7 +12,7 @@ import 'package:flutter_libmonero/wownero/wownero.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/add_token_view.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
import 'package:stackwallet/pages/add_wallet_views/restore_wallet_view/confirm_recovery_dialog.dart';
import 'package:stackwallet/pages/add_wallet_views/restore_wallet_view/sub_widgets/restore_failed_dialog.dart';
import 'package:stackwallet/pages/add_wallet_views/restore_wallet_view/sub_widgets/restore_succeeded_dialog.dart';
@ -351,7 +351,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
if (manager.coin == Coin.ethereum) {
unawaited(
Navigator.of(context).pushNamed(
AddTokenView.routeName,
EditWalletTokensView.routeName,
arguments: manager.walletId,
),
);

View file

@ -3,13 +3,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/coin_entity.dart';
import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart';
import 'package:stackwallet/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/global/wallets_service_provider.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/services/wallets_service.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -24,6 +21,7 @@ import 'package:stackwallet/widgets/eth_wallet_radio.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/wallet_info_row/wallet_info_row.dart';
import 'package:tuple/tuple.dart';
final newEthWalletTriggerTempUntilHiveCompletelyDeleted =
StateProvider((ref) => false);
@ -52,21 +50,12 @@ class _SelectWalletForTokenViewState
String? _selectedWalletId;
void _onContinue() {
final wallet = ref
.read(walletsChangeNotifierProvider)
.getManager(_selectedWalletId!)
.wallet as EthereumWallet;
final tokenSet = wallet.getWalletTokenContractAddresses().toSet();
tokenSet.add(widget.entity.token.address);
wallet.updateWalletTokenContractAddresses(tokenSet.toList());
Navigator.of(context).pushNamed(HomeView.routeName);
showFloatingFlushBar(
type: FlushBarType.success,
message:
"${widget.entity.name} (${widget.entity.ticker}) added to ${wallet.walletName}",
context: context,
Navigator.of(context).pushNamed(
EditWalletTokensView.routeName,
arguments: Tuple2(
_selectedWalletId!,
[widget.entity.token.address],
),
);
}
@ -170,17 +159,23 @@ class _SelectWalletForTokenViewState
leading: AppBarBackButton(),
),
body: SizedBox(
width: 480,
width: 500,
child: child,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (isDesktop)
const SizedBox(
height: 24,
),
Text(
"Select Ethereum wallet",
textAlign: TextAlign.center,
style: STextStyles.pageTitleH1(context),
style: isDesktop
? STextStyles.desktopH2(context)
: STextStyles.pageTitleH1(context),
),
SizedBox(
height: isDesktop ? 16 : 8,
@ -188,7 +183,9 @@ class _SelectWalletForTokenViewState
Text(
"You are adding an ETH token.",
textAlign: TextAlign.center,
style: STextStyles.subtitle(context),
style: isDesktop
? STextStyles.desktopSubtitleH2(context)
: STextStyles.subtitle(context),
),
const SizedBox(
height: 8,
@ -196,62 +193,77 @@ class _SelectWalletForTokenViewState
Text(
"You must choose an Ethereum wallet in order to use ${widget.entity.name}",
textAlign: TextAlign.center,
style: STextStyles.subtitle(context),
style: isDesktop
? STextStyles.desktopSubtitleH2(context)
: STextStyles.subtitle(context),
),
SizedBox(
height: isDesktop ? 60 : 16,
),
ethWalletIds.isEmpty
? RoundedWhiteContainer(
padding: EdgeInsets.all(isDesktop ? 16 : 12),
child: Text(
_hasEthWallets
? "All current Ethereum wallets already have ${widget.entity.name}"
: "You do not have any Ethereum wallets",
style: STextStyles.label(context),
style: isDesktop
? STextStyles.desktopSubtitleH2(context)
: STextStyles.label(context),
),
)
: Expanded(
child: Column(
children: [
RoundedWhiteContainer(
padding: const EdgeInsets.all(8),
child: ListView.separated(
itemCount: ethWalletIds.length,
shrinkWrap: true,
separatorBuilder: (_, __) => SizedBox(
height: isDesktop ? 12 : 6,
),
itemBuilder: (_, index) {
return RoundedContainer(
padding: const EdgeInsets.all(8),
onPressed: () {
setState(() {
_selectedWalletId = ethWalletIds[index];
});
},
color: isDesktop
? Colors.transparent
: _selectedWalletId == ethWalletIds[index]
? Theme.of(context)
.extension<StackColors>()!
.highlight
: Colors.transparent,
child: isDesktop
? EthWalletRadio(
walletId: ethWalletIds[index],
selectedWalletId: _selectedWalletId,
)
: WalletInfoRow(
walletId: ethWalletIds[index],
),
);
},
: ConditionalParent(
condition: !isDesktop,
builder: (child) => Expanded(
child: Column(
children: [
RoundedWhiteContainer(
padding: const EdgeInsets.all(8),
child: child,
),
),
],
],
),
),
child: ListView.separated(
itemCount: ethWalletIds.length,
shrinkWrap: true,
separatorBuilder: (_, __) => SizedBox(
height: isDesktop ? 12 : 6,
),
itemBuilder: (_, index) {
return RoundedContainer(
padding: EdgeInsets.all(isDesktop ? 16 : 8),
onPressed: () {
setState(() {
_selectedWalletId = ethWalletIds[index];
});
},
color: isDesktop
? Theme.of(context)
.extension<StackColors>()!
.popupBG
: _selectedWalletId == ethWalletIds[index]
? Theme.of(context)
.extension<StackColors>()!
.highlight
: Colors.transparent,
child: isDesktop
? EthWalletRadio(
walletId: ethWalletIds[index],
selectedWalletId: _selectedWalletId,
)
: WalletInfoRow(
walletId: ethWalletIds[index],
),
);
},
),
),
if (ethWalletIds.isEmpty)
if (ethWalletIds.isEmpty || isDesktop)
const SizedBox(
height: 16,
),
if (isDesktop)
const SizedBox(
height: 16,
),

View file

@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/add_token_view.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart';
import 'package:stackwallet/pages/add_wallet_views/select_wallet_for_token_view.dart';
import 'package:stackwallet/pages/add_wallet_views/verify_recovery_phrase_view/sub_widgets/word_table.dart';
@ -134,7 +134,7 @@ class _VerifyRecoveryPhraseViewState
if (widget.manager.coin == Coin.ethereum) {
unawaited(
Navigator.of(context).pushNamed(
AddTokenView.routeName,
EditWalletTokensView.routeName,
arguments: widget.manager.walletId,
),
);

View file

@ -3,7 +3,7 @@ import 'dart:async';
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_token_view.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
import 'package:stackwallet/pages/token_view/sub_widgets/my_tokens_list.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
@ -147,7 +147,7 @@ class _MyTokensViewState extends ConsumerState<MyTokensView> {
),
onPressed: () async {
final result = await Navigator.of(context).pushNamed(
AddTokenView.routeName,
EditWalletTokensView.routeName,
arguments: widget.walletId,
);

View file

@ -12,7 +12,7 @@ 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_token_view/edit_wallet_tokens_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';
import 'package:stackwallet/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart';
@ -217,17 +217,28 @@ class RouteGenerator {
builder: (_) => const AddWalletView(),
settings: RouteSettings(name: settings.name));
case AddTokenView.routeName:
case EditWalletTokensView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => AddTokenView(
builder: (_) => EditWalletTokensView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
} else if (args is Tuple2<String, List<String>>) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => EditWalletTokensView(
walletId: args.item1,
contractsToMarkSelected: args.item2,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");