diff --git a/lib/entities/desktop_dropdown_item.dart b/lib/entities/desktop_dropdown_item.dart new file mode 100644 index 000000000..3a542f5e8 --- /dev/null +++ b/lib/entities/desktop_dropdown_item.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; + +class DesktopDropdownItem { + final Function() onSelected; + final Widget child; + final bool isSelected; + + DesktopDropdownItem({required this.onSelected, required this.child, this.isSelected = false}); +} diff --git a/lib/reactions/on_authentication_state_change.dart b/lib/reactions/on_authentication_state_change.dart index f7078a45e..0adea5128 100644 --- a/lib/reactions/on_authentication_state_change.dart +++ b/lib/reactions/on_authentication_state_change.dart @@ -1,5 +1,4 @@ import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_view.dart'; import 'package:flutter/widgets.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/entities/load_current_wallet.dart'; diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart index fe98a0af9..1e714d992 100644 --- a/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart @@ -1,14 +1,18 @@ import 'package:another_flushbar/flushbar.dart'; +import 'package:cake_wallet/entities/desktop_dropdown_item.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; +import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; class DesktopWalletSelectionDropDown extends StatefulWidget { final WalletListViewModel walletListViewModel; @@ -25,85 +29,96 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24); final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24); - - final double tileHeight = 60; + Image _getNewWalletImage(BuildContext context) => Image.asset( + 'assets/images/new_wallet.png', + height: 12, + width: 12, + color: Theme.of(context).primaryTextTheme.headline6!.color!, + ); + Image _getRestoreWalletImage(BuildContext context) => Image.asset( + 'assets/images/restore_wallet.png', + height: 12, + width: 12, + color: Theme.of(context).primaryTextTheme.headline6!.color!, + ); Flushbar<void>? _progressBar; @override Widget build(BuildContext context) { final themeData = Theme.of(context); - return DropdownButton<WalletListItem>( - items: widget.walletListViewModel.wallets - .map((wallet) => DropdownMenuItem( - child: ConstrainedBox( - constraints: BoxConstraints(maxWidth: 500), - child: walletListItemTile(wallet), - ), + return Observer(builder: (context) { + final dropDownItems = [ + ...widget.walletListViewModel.wallets + .map((wallet) => DesktopDropdownItem( + isSelected: wallet.isCurrent, + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 500), + child: DropDownItemWidget( + title: wallet.name, + image: wallet.isEnabled ? _imageFor(type: wallet.type) : nonWalletTypeIcon), + ), + onSelected: () => _onSelectedWallet(wallet), + )) + .toList(), + DesktopDropdownItem( + onSelected: () => _navigateToCreateWallet(), + child: DropDownItemWidget( + title: S.of(context).create_new, + image: _getNewWalletImage(context), + ), + ), + DesktopDropdownItem( + onSelected: () => _navigateToRestoreWallet(), + child: DropDownItemWidget( + title: S.of(context).restore_wallet, + image: _getRestoreWalletImage(context), + ), + ), + ]; + + return DropdownButton<DesktopDropdownItem>( + items: dropDownItems + .map( + (wallet) => DropdownMenuItem<DesktopDropdownItem>( + child: wallet.child, value: wallet, - )) - .toList(), - onChanged: (selectedWallet) async { - if (selectedWallet!.isCurrent || !selectedWallet.isEnabled) { - return; - } - - final confirmed = await showPopUp<bool>( - context: context, - builder: (dialogContext) { - return AlertWithTwoActions( - alertTitle: S.of(context).change_wallet_alert_title, - alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name), - leftButtonText: S.of(context).cancel, - rightButtonText: S.of(context).change, - actionLeftButton: () => Navigator.of(context).pop(false), - actionRightButton: () => Navigator.of(context).pop(true)); - }) ?? - false; - - if (confirmed) { - await _loadWallet(selectedWallet); - } - }, - dropdownColor: themeData.textTheme.bodyText1?.decorationColor, - style: TextStyle(color: themeData.primaryTextTheme.headline6?.color), - selectedItemBuilder: (context) => widget.walletListViewModel.wallets - .map((wallet) => ConstrainedBox( - constraints: BoxConstraints(maxWidth: 500), - child: walletListItemTile(wallet), - )) - .toList(), - value: widget.walletListViewModel.wallets.firstWhere((element) => element.isCurrent), - underline: const SizedBox(), - focusColor: Colors.transparent, - ); + ), + ) + .toList(), + onChanged: (item) { + item?.onSelected(); + }, + dropdownColor: themeData.textTheme.bodyText1?.decorationColor, + style: TextStyle(color: themeData.primaryTextTheme.headline6?.color), + selectedItemBuilder: (context) => dropDownItems.map((item) => item.child).toList(), + value: dropDownItems.firstWhere((element) => element.isSelected), + underline: const SizedBox(), + focusColor: Colors.transparent, + ); + }); } - Widget walletListItemTile(WalletListItem wallet) { - return Container( - height: tileHeight, - padding: EdgeInsets.symmetric(horizontal: 20), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: <Widget>[ - wallet.isEnabled ? _imageFor(type: wallet.type) : nonWalletTypeIcon, - SizedBox(width: 10), - Flexible( - child: Text( - wallet.name, - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.w500, - color: Theme.of(context).primaryTextTheme.headline6!.color!, - ), - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - ) - ], - ), - ); + void _onSelectedWallet(WalletListItem selectedWallet) async { + if (selectedWallet.isCurrent || !selectedWallet.isEnabled) { + return; + } + final confirmed = await showPopUp<bool>( + context: context, + builder: (dialogContext) { + return AlertWithTwoActions( + alertTitle: S.of(context).change_wallet_alert_title, + alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name), + leftButtonText: S.of(context).cancel, + rightButtonText: S.of(context).change, + actionLeftButton: () => Navigator.of(context).pop(false), + actionRightButton: () => Navigator.of(context).pop(true)); + }) ?? + false; + + if (confirmed) { + await _loadWallet(selectedWallet); + } } Image _imageFor({required WalletType type}) { @@ -152,6 +167,24 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD } } + void _navigateToCreateWallet() { + if (isSingleCoin) { + Navigator.of(context) + .pushNamed(Routes.newWallet, arguments: widget.walletListViewModel.currentWalletType); + } else { + Navigator.of(context).pushNamed(Routes.newWalletType); + } + } + + void _navigateToRestoreWallet() { + if (isSingleCoin) { + Navigator.of(context) + .pushNamed(Routes.restoreWallet, arguments: widget.walletListViewModel.currentWalletType); + } else { + Navigator.of(context).pushNamed(Routes.restoreWalletType); + } + } + void changeProcessText(String text) { _progressBar = createBar<void>(text, duration: null)..show(context); } diff --git a/lib/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart b/lib/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart new file mode 100644 index 000000000..47efd04a7 --- /dev/null +++ b/lib/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +class DropDownItemWidget extends StatelessWidget { + const DropDownItemWidget({super.key, required this.title, required this.image}); + final double tileHeight = 60; + final Image image; + final String title; + + @override + Widget build(BuildContext context) { + return Container( + height: tileHeight, + padding: EdgeInsets.symmetric(horizontal: 20), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: <Widget>[ + image, + SizedBox(width: 10), + Flexible( + child: Text( + title, + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w500, + color: Theme.of(context).primaryTextTheme.headline6!.color!, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ) + ], + ), + ); + } +} diff --git a/lib/src/screens/settings/desktop_settings/dashboard_settings_page.dart b/lib/src/screens/settings/desktop_settings/dashboard_settings_page.dart index 69cd2ad27..8028e6057 100644 --- a/lib/src/screens/settings/desktop_settings/dashboard_settings_page.dart +++ b/lib/src/screens/settings/desktop_settings/dashboard_settings_page.dart @@ -16,7 +16,8 @@ class DesktopSettingsPage extends StatefulWidget { } class _DesktopSettingsPageState extends State<DesktopSettingsPage> { - final int itemCount = SettingActions.all.length; + final int itemCount = SettingActions.desktopSettings.length; + int? currentPage; void _onItemChange(int index) { @@ -48,8 +49,8 @@ class _DesktopSettingsPageState extends State<DesktopSettingsPage> { child: ListView.separated( padding: EdgeInsets.only(top: 0), itemBuilder: (_, index) { - final item = SettingActions.all[index]; - final isLastTile = index == itemCount; + final item = SettingActions.desktopSettings[index]; + final isLastTile = index == itemCount - 1; return SettingActionButton( isLastTile: isLastTile, selectionActive: currentPage != null, diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 235b59fbc..8acc14d2f 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -234,7 +234,7 @@ class WalletListBodyState extends State<WalletListBody> { auth.hideProgressText(); auth.close(); WidgetsBinding.instance.addPostFrameCallback((_) { - navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false); + Navigator.of(context).pop(); }); } catch (e) { auth.changeProcessText( @@ -246,7 +246,7 @@ class WalletListBodyState extends State<WalletListBody> { changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name)); await widget.walletListViewModel.loadWallet(wallet); hideProgressText(); - navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false); + Navigator.of(context).pop(); } catch (e) { changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); } diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 7e0727312..9db864343 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -1,3 +1,6 @@ +import 'dart:io'; + +import 'package:cake_wallet/utils/device_info.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; @@ -100,7 +103,8 @@ class AddressTextField extends StatelessWidget { width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: DeviceInfo.instance.isMobile + ? MainAxisAlignment.spaceBetween : MainAxisAlignment.end, children: [ SizedBox(width: 5), if (this.options.contains(AddressTextFieldOption.paste)) ...[ @@ -130,7 +134,8 @@ class AddressTextField extends StatelessWidget { )), )), ], - if (this.options.contains(AddressTextFieldOption.qrCode)) ...[ + if (this.options.contains(AddressTextFieldOption.qrCode) && DeviceInfo.instance.isMobile) + ...[ Container( width: prefixIconWidth, height: prefixIconHeight, @@ -156,7 +161,7 @@ class AddressTextField extends StatelessWidget { .decorationColor!, )), )) - ], + ] else SizedBox(width: 5), if (this .options .contains(AddressTextFieldOption.addressBook)) ...[ diff --git a/lib/src/widgets/setting_actions.dart b/lib/src/widgets/setting_actions.dart index 76e5d2c76..043c8bd0e 100644 --- a/lib/src/widgets/setting_actions.dart +++ b/lib/src/widgets/setting_actions.dart @@ -24,6 +24,16 @@ class SettingActions { supportSettingAction, ]; + static List<SettingActions> desktopSettings = [ + connectionSettingAction, + addressBookSettingAction, + securityBackupSettingAction, + privacySettingAction, + displaySettingAction, + otherSettingAction, + supportSettingAction, + ]; + static SettingActions connectionSettingAction = SettingActions._( name: S.current.connection_sync, image: 'assets/images/nodes_menu.png', diff --git a/lib/utils/device_info.dart b/lib/utils/device_info.dart new file mode 100644 index 000000000..144ea3fa4 --- /dev/null +++ b/lib/utils/device_info.dart @@ -0,0 +1,11 @@ +import 'dart:io'; + +class DeviceInfo { + DeviceInfo._(); + + static DeviceInfo get instance => DeviceInfo._(); + + bool get isMobile => Platform.isAndroid || Platform.isIOS; + + bool get isDesktop => Platform.isMacOS || Platform.isWindows || Platform.isLinux; +} \ No newline at end of file diff --git a/lib/view_model/wallet_list/wallet_list_item.dart b/lib/view_model/wallet_list/wallet_list_item.dart index af30b9bea..a644c07b3 100644 --- a/lib/view_model/wallet_list/wallet_list_item.dart +++ b/lib/view_model/wallet_list/wallet_list_item.dart @@ -1,13 +1,13 @@ -import 'package:flutter/foundation.dart'; import 'package:cw_core/wallet_type.dart'; class WalletListItem { - const WalletListItem( - {required this.name, - required this.type, - required this.key, - this.isCurrent = false, - this.isEnabled = true}); + const WalletListItem({ + required this.name, + required this.type, + required this.key, + this.isCurrent = false, + this.isEnabled = true, + }); final String name; final WalletType type; diff --git a/lib/view_model/wallet_list/wallet_list_view_model.dart b/lib/view_model/wallet_list/wallet_list_view_model.dart index 6d63675ba..fb6fe4c22 100644 --- a/lib/view_model/wallet_list/wallet_list_view_model.dart +++ b/lib/view_model/wallet_list/wallet_list_view_model.dart @@ -1,5 +1,9 @@ import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/wallet_loading_service.dart'; +import 'package:cw_core/balance.dart'; +import 'package:cw_core/transaction_history.dart'; +import 'package:cw_core/transaction_info.dart'; +import 'package:cw_core/wallet_base.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/di.dart'; @@ -22,6 +26,7 @@ abstract class WalletListViewModelBase with Store { this._authService, ) : wallets = ObservableList<WalletListItem>() { _updateList(); + reaction((_) => _appStore.wallet, (_) => _updateList()); } @observable