Merge branch 'dashboard-desktop-view' of https://github.com/cake-tech/cake_wallet into align-create-restore-screens

This commit is contained in:
Godwin Asuquo 2023-02-23 17:51:26 +02:00
commit 28c599e68a
12 changed files with 226 additions and 104 deletions

View file

@ -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});
}

View file

@ -23,8 +23,11 @@ void startAuthenticationStateChange(AuthenticationStore authenticationStore,
}
if (state == AuthenticationState.allowed) {
await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
return;
// Temporary workaround for the issue with desktopKey dispose
Future.delayed(Duration(milliseconds: 2), () async {
await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
return;
});
}
});
}

View file

@ -25,6 +25,8 @@ class DesktopSidebarWrapper extends BasePage {
required this.dashboardViewModel,
});
static Key _pageViewKey = GlobalKey();
@override
PreferredSizeWidget desktopAppBar(BuildContext context) => DesktopNavbar(
leading: Padding(
@ -97,23 +99,7 @@ class DesktopSidebarWrapper extends BasePage {
@override
Widget body(BuildContext context) {
reaction<SidebarItem>((_) => desktopSidebarViewModel.currentPage, (page) {
String? currentPath;
DesktopDashboardPage.desktopKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
if (page == SidebarItem.transactions) {
return;
}
if (currentPath == Routes.transactionsPage) {
Navigator.of(DesktopDashboardPage.desktopKey.currentContext!).pop();
}
pageController.jumpToPage(page.index);
});
_setEffects();
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
@ -143,6 +129,7 @@ class DesktopSidebarWrapper extends BasePage {
}),
Expanded(
child: PageView(
key: _pageViewKey,
controller: pageController,
physics: NeverScrollableScrollPhysics(),
children: [
@ -174,4 +161,25 @@ class DesktopSidebarWrapper extends BasePage {
],
);
}
final desktopKey = DesktopDashboardPage.desktopKey;
void _setEffects() async {
reaction<SidebarItem>((_) => desktopSidebarViewModel.currentPage, (page) {
String? currentPath;
desktopKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
if (page == SidebarItem.transactions) {
return;
}
if (currentPath == Routes.transactionsPage) {
Navigator.of(desktopKey.currentContext!).pop();
}
pageController.jumpToPage(page.index);
});
}
}

View file

@ -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);
}

View file

@ -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,
),
)
],
),
);
}
}

View file

@ -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,

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/show_bar.dart';
@ -100,9 +101,9 @@ class WalletListBodyState extends State<WalletListBody> {
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () =>
Navigator.of(context).pop(false),
Navigator.of(dialogContext).pop(false),
actionRightButton: () =>
Navigator.of(context).pop(true));
Navigator.of(dialogContext).pop(true));
}) ??
false;

View file

@ -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)) ...[

View file

@ -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',

View file

@ -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;
}

View file

@ -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;

View file

@ -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