mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Cw 1551 quick bitcoin wallet sync (#3446)
* add Wallet Accounts page and integrate UI * account model refactor * add walletInfoAccount table and API * add WalletAccountList API * add multi-account support for Electrum wallets * add Bitcoin lightning card and card style handling * refine account filtering and card selection * update current account balance on wallet changes * update configure.dar * Use named parameters for card design * Add Bitcoin account handling and refactor lookup * refactor dashboard * refactor cards UI and account balance handling * Improve Bitcoin account selection and balances * restrict unspent coin usage to current account * filter transactions by current Bitcoin account * refactor electrum wallet * refactor address generation * add accountIndex to address fetch logic * Update configure.dart * reload transactions on Bitcoin account change * restore multiple BTC accounts and improve fetch * show account balance with in btc * track account index in Bitcoin transactions * discover btc BIP39 accounts during restore * fix account switching and card ordering bugs * fix card customizer * limit account name length in modal * update BTC account list balance display * auto-reformat * formating fix * skip HD map validation * fix 0 balance issue * minor fix * quick bitcoin wallet sync * restore shuffle change output * restore address lookup fix * Update electrum.dart * remove account customizer * Merge branch 'CW-1142-Add-accounts-feature-to-BTC' into CW-1551-Quick-Bitcoin-Wallet-Sync * probe only segwit in account discovery * add account discovery limit and tracking * Improve Electrum sync account handling [skip ci] * remove legacy monero account and address list UI * rework BTC account balance refres * avoid unnecessary BTC account reloads * refactor account header in addresses page * fix blank popup when editing an account * fix: dedupe UTXO balance * minor fix * add Bitcoin current account API * Apply batched suggestions from code review [skip ci] Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * address PR review comments [skip ci] * remove account customizer and fix accounts init * restrict extra BTC accounts to SegWit * add BTC multi-account toggle * Fix BTC sync gating and account balance update * minor fix * Update electrum_wallet.dart * store selected account on WalletInfo * Refine wallet account reset visibility * refactor BTC address prep and move accounts page * refactor account stack and customizer routing * migrate legacy Lightning card styles * unify dashboard account change state * fix account routing and Electrum balances * fix account setup and wallet cards UI * disable Bitcoin Accounts for all hardware * merge conflicts fix * minor fixes * update cards_view [skip ci] * reverted cards view update [skip ci] * fix asset name * moved the transaction filtering into the electrum wallet * localization * disable accounts for watch-only wallets * fix: stop card render from switching the wallet account * fix: restore Lightning transactions in tx history * minor fix * fix: skip duplicate legacy address generation [skip ci] * fix: correct account balance/design mismatch bugs * addressing review comments --------- Co-authored-by: Robert Malikowski <malikowskirobert@gmail.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
Omar Hatem
Robert Malikowski
parent
0503d54290
commit
d7ebf42814
@@ -1,505 +0,0 @@
|
||||
import 'dart:ui';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/pages/card_customizer.dart';
|
||||
import 'package:cake_wallet/new-ui/viewmodels/card_customizer/card_customizer_bloc.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/cards/balance_card.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modal_grab_handle.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
|
||||
import 'package:cw_core/balance_card_style_settings.dart';
|
||||
import 'package:cw_core/card_design.dart';
|
||||
import 'package:cw_core/generate_name.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class AccountCustomizerListItem {
|
||||
final BalanceCard card;
|
||||
final int order;
|
||||
final AccountListItem accountListItem;
|
||||
|
||||
AccountCustomizerListItem(
|
||||
{required this.card, required this.order, required this.accountListItem});
|
||||
}
|
||||
|
||||
class AccountCustomizer extends StatefulWidget {
|
||||
const AccountCustomizer(
|
||||
{super.key, required this.accountListViewModel, required this.dashboardViewModel});
|
||||
|
||||
final MoneroAccountListViewModel accountListViewModel;
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
|
||||
@override
|
||||
State<AccountCustomizer> createState() => _AccountCustomizerState();
|
||||
}
|
||||
|
||||
class _AccountCustomizerState extends State<AccountCustomizer> {
|
||||
static const double _kStackVisibleFactor = 0.2;
|
||||
late final double cardWidth = MediaQuery.of(context).size.width * 0.9;
|
||||
|
||||
final List<AccountCustomizerListItem> _items = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadCards();
|
||||
final activeId = widget.accountListViewModel.selected.id;
|
||||
for (int i = 0; i < _items.length - 1; i++) {
|
||||
if (_items[i].accountListItem.id == activeId) {
|
||||
final lastIndex = _items.length - 1;
|
||||
final temp = _items[i];
|
||||
_items[i] = _items[lastIndex];
|
||||
_items[lastIndex] = temp;
|
||||
saveCardOrder();
|
||||
widget.dashboardViewModel.loadCardDesigns();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
saveCardOrder().then((value) => widget.dashboardViewModel.loadCardDesigns());
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void loadCards() {
|
||||
_items.clear();
|
||||
|
||||
final accounts = widget.accountListViewModel.accounts;
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
final index = widget.dashboardViewModel.cardOrder[i];
|
||||
|
||||
if (index == null || index >= accounts.length) {
|
||||
// db order broken.
|
||||
reset();
|
||||
break;
|
||||
}
|
||||
|
||||
_items.add(AccountCustomizerListItem(
|
||||
card: BalanceCard(
|
||||
accountName: accounts[index].label,
|
||||
accountIndex: accounts[index].id,
|
||||
balance: accounts[index].balance ?? "0.00",
|
||||
accountBalance: accounts[index].balance ?? "0.00",
|
||||
designSwitchDuration: Duration.zero,
|
||||
assetName: widget.accountListViewModel.currency.title,
|
||||
onCustomizeTapped: (i == accounts.length - 1) ? _openCardCustomizer : null,
|
||||
selected: i == accounts.length - 1,
|
||||
width: cardWidth,
|
||||
design: widget.dashboardViewModel.cardDesigns[index],
|
||||
),
|
||||
order: index,
|
||||
accountListItem: accounts[index]));
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_items.isEmpty) return SizedBox.shrink();
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24))),
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: S.of(context).wallet_accounts,
|
||||
leadingIcon: Icon(Icons.close),
|
||||
leadingSemanticLabel: S.of(context).close,
|
||||
onLeadingPressed: Navigator.of(context).maybePop,
|
||||
trailingIcon: Icon(Icons.refresh),
|
||||
trailingSemanticLabel: S.of(context).reset,
|
||||
onTrailingPressed: showResetDialog,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 24.0),
|
||||
child: Text(
|
||||
S.of(context).account_customizer_desc,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
ReorderableListView.builder(
|
||||
padding: EdgeInsets.only(bottom: 196),
|
||||
scrollController: ModalScrollController.of(context),
|
||||
onReorder: reorder,
|
||||
proxyDecorator: (child, index, animation) {
|
||||
return AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (context, _) {
|
||||
final animValue = Curves.easeOutCubic.transform(animation.value);
|
||||
final scale = lerpDouble(1, 1.05, animValue)!;
|
||||
|
||||
return Opacity(
|
||||
opacity: 1 - animValue.clamp(0.0, 0.1),
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: cardWidth,
|
||||
child: Transform.scale(
|
||||
scale: scale,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: _items[index].card,
|
||||
);
|
||||
},
|
||||
itemCount: _items.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final card = _items[index].card;
|
||||
// The stack is ordered bottom to top, so the last item
|
||||
// is the account currently in front — the selected one.
|
||||
final selectedItemIndex = _items.length - 1;
|
||||
|
||||
return Container(
|
||||
key: ValueKey(index),
|
||||
// One labeled, selectable node per account; the card's own
|
||||
// texts stay reachable underneath it.
|
||||
child: Semantics(
|
||||
button: true,
|
||||
selected: selectedItemIndex == index,
|
||||
label: _items[index].accountListItem.label,
|
||||
onTap: () => reorder(index, _items.length),
|
||||
child: GestureDetector(
|
||||
excludeFromSemantics: true,
|
||||
onTap: () {
|
||||
reorder(index, _items.length);
|
||||
},
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
heightFactor: _kStackVisibleFactor,
|
||||
child: card,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: 50),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: MergeSemantics(
|
||||
child: Semantics(
|
||||
button: true,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(999999),
|
||||
onTap: _showAddAccountModal,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(999999)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 18.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add,
|
||||
size: 28,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
Text(
|
||||
S.of(context).add_account,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.w500),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)))),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool _checkReadyToManage() {
|
||||
if (widget.dashboardViewModel.status is! SyncedSyncStatus) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).wallet_is_syncing,
|
||||
alertContent: S.of(context).cannot_manage_accounts_during_sync,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: Navigator.of(context).pop));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> _showAddAccountModal() async {
|
||||
if (!_checkReadyToManage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final res = await showCupertinoModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) {
|
||||
final modal = getIt.get<AccountCreationModal>();
|
||||
return Material(child: modal);
|
||||
});
|
||||
if (res != null && res is bool && res == true) {
|
||||
await widget.dashboardViewModel.loadCardDesigns();
|
||||
loadCards();
|
||||
await saveCardOrder();
|
||||
}
|
||||
}
|
||||
|
||||
void _openCardCustomizer() {
|
||||
if (!_checkReadyToManage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final bloc = getIt.get<CardCustomizerBloc>(param1: false);
|
||||
|
||||
Navigator.of(context).push(CupertinoPageRoute(
|
||||
builder: (context) {
|
||||
return BlocProvider(
|
||||
create: (context) => bloc,
|
||||
child: Material(
|
||||
child: CardCustomizer(
|
||||
cryptoTitle: widget.dashboardViewModel.wallet.currency.fullName ??
|
||||
widget.dashboardViewModel.wallet.currency.name,
|
||||
cryptoName: widget.dashboardViewModel.wallet.currency.name,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)).then((_) async {
|
||||
bloc.add(DesignSaved());
|
||||
await bloc.stream.firstWhere((item) => item is CardCustomizerSaved);
|
||||
await widget.dashboardViewModel.loadCardDesigns();
|
||||
loadCards();
|
||||
});
|
||||
}
|
||||
|
||||
void reorder(int oldIndex, int newIndex) {
|
||||
setState(() {
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final AccountCustomizerListItem item = _items.removeAt(oldIndex);
|
||||
_items.insert(newIndex, item);
|
||||
});
|
||||
|
||||
// necessary to copy all this to keep constant constructor for BalanceCard
|
||||
for (int i = 0; i < _items.length; i++) {
|
||||
_items[i] = AccountCustomizerListItem(
|
||||
card: BalanceCard(
|
||||
accountName: _items[i].card.accountName,
|
||||
balance: _items[i].card.balance,
|
||||
accountIndex: _items[i].card.accountIndex,
|
||||
accountBalance: _items[i].card.accountBalance,
|
||||
assetName: _items[i].card.assetName,
|
||||
designSwitchDuration: _items[i].card.designSwitchDuration,
|
||||
onCustomizeTapped: (i == _items.length - 1) ? _openCardCustomizer : null,
|
||||
selected: i == _items.length - 1,
|
||||
width: _items[i].card.width,
|
||||
design: _items[i].card.design,
|
||||
),
|
||||
order: i,
|
||||
accountListItem: _items[i].accountListItem);
|
||||
}
|
||||
|
||||
if (newIndex == _items.length - 1 || oldIndex == _items.length - 1) {
|
||||
widget.accountListViewModel.select(_items[_items.length - 1].accountListItem);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveCardOrder() async {
|
||||
for (int i = 0; i < _items.length; i++) {
|
||||
final idx = _items.indexWhere((element) => element.accountListItem.id == i);
|
||||
printV("$i: $idx");
|
||||
|
||||
await BalanceCardStyleSettings.fromCardDesign(
|
||||
walletInfoId: widget.dashboardViewModel.wallet.walletInfo.internalId,
|
||||
accountIndex: i,
|
||||
cardOrder: idx,
|
||||
design: _items[idx].card.design)
|
||||
.insert();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showResetDialog() async {
|
||||
final res = await showPopUp(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).reset,
|
||||
alertContent: S.of(context).card_order_reset_desc,
|
||||
leftButtonText: S.of(context).yes,
|
||||
rightButtonText: S.of(context).no,
|
||||
actionLeftButton: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
actionRightButton: Navigator.of(context).pop);
|
||||
});
|
||||
if (res != null && res is bool && res) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reset() async {
|
||||
_items.clear();
|
||||
|
||||
final accounts = widget.accountListViewModel.accounts;
|
||||
for (int i = 0; i < widget.accountListViewModel.accounts.length; i++) {
|
||||
_items.add(AccountCustomizerListItem(
|
||||
card: BalanceCard(
|
||||
accountName: accounts[i].label,
|
||||
accountIndex: accounts[i].id,
|
||||
balance: accounts[i].balance ?? "0.00",
|
||||
accountBalance: accounts[i].balance ?? "0.00",
|
||||
assetName: widget.accountListViewModel.currency.title,
|
||||
selected: true,
|
||||
designSwitchDuration: Duration(milliseconds: 200),
|
||||
width: cardWidth,
|
||||
design: i >= widget.dashboardViewModel.cardDesigns.length
|
||||
? CardDesign.genericDefault
|
||||
: widget.dashboardViewModel.cardDesigns[i],
|
||||
),
|
||||
order: i,
|
||||
accountListItem: accounts[i]));
|
||||
}
|
||||
|
||||
saveCardOrder();
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
class AccountCreationModal extends StatefulWidget {
|
||||
const AccountCreationModal({super.key, required this.accountEditOrCreateViewModel});
|
||||
|
||||
final MoneroAccountEditOrCreateViewModel accountEditOrCreateViewModel;
|
||||
|
||||
@override
|
||||
State<AccountCreationModal> createState() => _AccountCreationModalState();
|
||||
}
|
||||
|
||||
class _AccountCreationModalState extends State<AccountCreationModal> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
bool _loading = false;
|
||||
|
||||
Future<void> _generateAccountName() async => _controller.text = await generateName();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(30))),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ModalGrabHandle(),
|
||||
ModalTopBar(title: S.of(context).create_account),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Column(
|
||||
spacing: 50,
|
||||
children: [
|
||||
SizedBox(),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16)),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
decoration: InputDecoration(hintText: S.of(context).account_name),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Semantics(
|
||||
button: true,
|
||||
label: S.of(context).generate_name,
|
||||
onTap: _generateAccountName,
|
||||
child: ExcludeSemantics(
|
||||
child: GestureDetector(
|
||||
onTap: _generateAccountName,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(5)),
|
||||
child: CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/randomize.svg",
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.primary, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
NewPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (_loading) return;
|
||||
setState(() {
|
||||
_loading = true;
|
||||
});
|
||||
widget.accountEditOrCreateViewModel.label = _controller.text;
|
||||
await widget.accountEditOrCreateViewModel.save();
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
text: S.of(context).continue_text,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isLoading: _loading,
|
||||
),
|
||||
SizedBox(),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,12 @@ class _NewAddressesPageState extends State<NewAddressesPage> {
|
||||
});
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
reaction((context) => widget.dashboardViewModel.cardDesigns.first, (value) {
|
||||
reaction((_) {
|
||||
final index = widget.dashboardViewModel.accountListViewModel?.selectedAccount?.id ?? 0;
|
||||
final designs = widget.dashboardViewModel.cardDesigns;
|
||||
return (index >= 0 && index < designs.length) ? designs[index] : null;
|
||||
}, (value) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
design = value;
|
||||
});
|
||||
@@ -130,8 +135,7 @@ class _NewAddressesPageState extends State<NewAddressesPage> {
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
if (widget.dashboardViewModel.type == WalletType.monero ||
|
||||
widget.dashboardViewModel.type == WalletType.wownero)
|
||||
if (widget.dashboardViewModel.isMultiAccountsEnabled)
|
||||
Observer(
|
||||
builder: (_) => AccountPreviewHeader(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
@@ -271,8 +275,7 @@ class AccountPreviewHeader extends StatelessWidget {
|
||||
final CardDesign? design;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
Widget build(BuildContext context) => Container(
|
||||
height: 64,
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
decoration: BoxDecoration(
|
||||
@@ -295,9 +298,7 @@ class AccountPreviewHeader extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
monero?.getCurrentAccount(dashboardViewModel.wallet).label ??
|
||||
wownero?.getCurrentAccount(dashboardViewModel.wallet).label ??
|
||||
"",
|
||||
dashboardViewModel.accountListViewModel?.selectedAccount?.label ?? '',
|
||||
style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
Text(
|
||||
@@ -325,7 +326,6 @@ class AccountPreviewHeader extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AddressRow extends StatelessWidget {
|
||||
|
||||
@@ -1,21 +1,77 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import "dart:async";
|
||||
import "dart:math";
|
||||
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/viewmodels/card_customizer/card_customizer_bloc.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/cards/balance_card.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cw_core/card_design.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import "package:cake_wallet/di.dart";
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/new-ui/modal_navigator.dart";
|
||||
import "package:cake_wallet/new-ui/viewmodels/card_customizer/card_customizer_bloc.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/cards/balance_card.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
|
||||
import "package:cake_wallet/src/widgets/cake_image_widget.dart";
|
||||
import "package:cake_wallet/view_model/dashboard/dashboard_view_model.dart";
|
||||
import "package:cw_core/card_design.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_bloc/flutter_bloc.dart";
|
||||
import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
|
||||
|
||||
class CardCustomizer extends StatefulWidget {
|
||||
const CardCustomizer({super.key, required this.cryptoTitle, required this.cryptoName});
|
||||
const CardCustomizer({required this.cryptoTitle, required this.cryptoName, super.key});
|
||||
|
||||
final String cryptoTitle;
|
||||
final String cryptoName;
|
||||
|
||||
static Future<void> show({
|
||||
required BuildContext context,
|
||||
required DashboardViewModel dashboardViewModel,
|
||||
required bool lightningMode,
|
||||
bool asModalSheet = true,
|
||||
VoidCallback? onSaved,
|
||||
}) async {
|
||||
final bloc = getIt.get<CardCustomizerBloc>(
|
||||
param1: lightningMode,
|
||||
param2: dashboardViewModel.settingsStore.displayAmountsInSatoshi,
|
||||
);
|
||||
|
||||
final customizer = BlocProvider(
|
||||
create: (_) => bloc,
|
||||
child: Material(
|
||||
child: CardCustomizer(
|
||||
cryptoTitle: dashboardViewModel.wallet.currency.fullName ??
|
||||
dashboardViewModel.wallet.currency.name,
|
||||
cryptoName: dashboardViewModel.wallet.currency.name,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (asModalSheet) {
|
||||
await CupertinoScaffold.showCupertinoModalBottomSheet(
|
||||
barrierColor: Colors.black.withAlpha(60),
|
||||
context: context,
|
||||
builder: (context) => ModalNavigator(
|
||||
parentContext: context,
|
||||
heightMode: ModalHeightModes.fullScreen,
|
||||
rootPage: customizer,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(builder: (_) => customizer),
|
||||
);
|
||||
}
|
||||
|
||||
bloc.add(DesignSaved());
|
||||
|
||||
await bloc.stream.firstWhere(
|
||||
(state) => state is CardCustomizerSaved,
|
||||
orElse: () => bloc.state,
|
||||
);
|
||||
|
||||
await dashboardViewModel.loadCardDesigns();
|
||||
await dashboardViewModel.accountListViewModel?.reload();
|
||||
|
||||
onSaved?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
State<CardCustomizer> createState() => _CardCustomizerState();
|
||||
}
|
||||
@@ -63,298 +119,303 @@ class _CardCustomizerState extends State<CardCustomizer> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<CardCustomizerBloc, CardCustomizerState>(
|
||||
listenWhen: (previous, current) => previous.accountName != current.accountName,
|
||||
listener: (context, state) {
|
||||
if (accountNameController.text != state.accountName) {
|
||||
accountNameController.text = state.accountName;
|
||||
}
|
||||
},
|
||||
child: BlocBuilder<CardCustomizerBloc, CardCustomizerState>(
|
||||
builder: (context, state) {
|
||||
if (state is CardCustomizerNotLoaded) return SizedBox.shrink();
|
||||
return PopScope(
|
||||
child: SingleChildScrollView(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
spacing: 25.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: editEnabled ? S.of(context).edit_account : S.of(context).edit_card,
|
||||
leadingIcon: Icon(Icons.close),
|
||||
leadingSemanticLabel: S.of(context).close,
|
||||
// trailingIcon: editEnabled ? Icon(Icons.delete_forever) : null,
|
||||
onLeadingPressed: () => Navigator.of(context).maybePop(),
|
||||
// onTrailingPressed: () {},
|
||||
),
|
||||
if (editEnabled)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).account_name),
|
||||
TextField(
|
||||
maxLength: 32,
|
||||
decoration: InputDecoration(counterText: ""),
|
||||
onChanged: (value) {
|
||||
context.read<CardCustomizerBloc>().add(AccountNameChanged(value));
|
||||
},
|
||||
controller: accountNameController,
|
||||
)
|
||||
],
|
||||
),
|
||||
Widget build(BuildContext context) => BlocListener<CardCustomizerBloc, CardCustomizerState>(
|
||||
listenWhen: (previous, current) => previous.accountName != current.accountName,
|
||||
listener: (context, state) {
|
||||
if (accountNameController.text != state.accountName) {
|
||||
accountNameController.text = state.accountName;
|
||||
}
|
||||
},
|
||||
child: BlocBuilder<CardCustomizerBloc, CardCustomizerState>(
|
||||
builder: (context, state) {
|
||||
if (state is CardCustomizerNotLoaded) return SizedBox.shrink();
|
||||
return PopScope(
|
||||
child: SingleChildScrollView(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
spacing: 25.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: editEnabled ? S.of(context).edit_account : S.of(context).edit_card,
|
||||
leadingIcon: Icon(Icons.close),
|
||||
leadingSemanticLabel: S.of(context).close,
|
||||
// trailingIcon: editEnabled ? Icon(Icons.delete_forever) : null,
|
||||
onLeadingPressed: () => Navigator.of(context).maybePop(),
|
||||
// onTrailingPressed: () {},
|
||||
),
|
||||
BalanceCard(
|
||||
width: min(MediaQuery.of(context).size.width * 0.87, 768),
|
||||
selected: true,
|
||||
designSwitchDuration: Duration(milliseconds: 300),
|
||||
accountName: editEnabled ? state.accountName : "",
|
||||
balance: "0.00",
|
||||
assetName: state.displaySats ? "sats" : widget.cryptoName,
|
||||
capitalizeAssetName: !state.displaySats,
|
||||
design: state.selectedDesign,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
if (editEnabled)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).card_style),
|
||||
Container(
|
||||
height: 63,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.availableDesigns.length,
|
||||
separatorBuilder: (context, index) {
|
||||
return SizedBox(width: 8.0);
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(CardDesignSelected(index));
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedSuperellipseBorder(
|
||||
side: BorderSide(
|
||||
color:
|
||||
((index == state.selectedDesignIndex)
|
||||
Text(S.of(context).account_name),
|
||||
TextField(
|
||||
maxLength: 32,
|
||||
decoration: InputDecoration(counterText: ""),
|
||||
onChanged: (value) {
|
||||
context.read<CardCustomizerBloc>().add(AccountNameChanged(value));
|
||||
},
|
||||
controller: accountNameController,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
BalanceCard(
|
||||
width: min(MediaQuery.of(context).size.width * 0.87, 768),
|
||||
selected: true,
|
||||
designSwitchDuration: Duration(milliseconds: 300),
|
||||
accountName: editEnabled ? state.accountName : "",
|
||||
balance: "0.00",
|
||||
assetName: state.displaySats ? "sats" : widget.cryptoName,
|
||||
capitalizeAssetName: !state.displaySats,
|
||||
design: state.selectedDesign,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).card_style),
|
||||
SizedBox(
|
||||
height: 63,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.availableDesigns.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
const SizedBox(width: 8),
|
||||
itemBuilder: (context, index) => GestureDetector(
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(CardDesignSelected(index));
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedSuperellipseBorder(
|
||||
side: BorderSide(
|
||||
color: ((index ==
|
||||
state.selectedDesignIndex)
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
: Colors.transparent),
|
||||
width: 1),
|
||||
borderRadius:
|
||||
BorderRadiusGeometry.circular(12),
|
||||
width: 1),
|
||||
borderRadius:
|
||||
BorderRadiusGeometry.circular(12),
|
||||
),
|
||||
),
|
||||
child: AnimatedScale(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
scale: index == state.selectedDesignIndex
|
||||
? 0.94
|
||||
: 1,
|
||||
child: BalanceCard(
|
||||
width: 96,
|
||||
borderRadius: 10,
|
||||
selected: false,
|
||||
designSwitchDuration:
|
||||
const Duration(milliseconds: 300),
|
||||
design:
|
||||
_cardStylePreviewDesign(state, index),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: AnimatedScale(
|
||||
duration: Duration(milliseconds: 200),
|
||||
scale: index == state.selectedDesignIndex
|
||||
? 0.94
|
||||
: 1,
|
||||
child: BalanceCard(
|
||||
width: 96,
|
||||
borderRadius: 10,
|
||||
selected: false,
|
||||
designSwitchDuration:
|
||||
Duration(milliseconds: 300),
|
||||
design: _cardStylePreviewDesign(state, index),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
)),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
switchInCurve: Curves.easeOut,
|
||||
switchOutCurve: Curves.easeIn,
|
||||
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||
return FadeTransition(
|
||||
)),
|
||||
),
|
||||
],
|
||||
)),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
switchInCurve: Curves.easeOut,
|
||||
switchOutCurve: Curves.easeIn,
|
||||
transitionBuilder: (child, Animation<double> animation) =>
|
||||
FadeTransition(
|
||||
opacity: animation,
|
||||
child: SizeTransition(
|
||||
sizeFactor: animation,
|
||||
axisAlignment: -1,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: _showIconStylePanel(state)
|
||||
? Column(
|
||||
key: const ValueKey('icon_style_panel'),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).icon_style),
|
||||
SizedBox(
|
||||
height: 48,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.availableIconPaths.length,
|
||||
separatorBuilder: (context, _) =>
|
||||
const SizedBox(width: 8.0),
|
||||
itemBuilder: (context, index) {
|
||||
final icon = state.availableIconPaths[index];
|
||||
final isSelected =
|
||||
index == state.selectedIconIndex;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(IconStyleSelected(index));
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration:
|
||||
const Duration(milliseconds: 200),
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
color: Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant
|
||||
.withAlpha(64)
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHigh,
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
),
|
||||
child: _showIconStylePanel(state)
|
||||
? Column(
|
||||
key: const ValueKey("icon_style_panel"),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).icon_style),
|
||||
SizedBox(
|
||||
height: 48,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.availableIconPaths.length,
|
||||
separatorBuilder: (context, _) =>
|
||||
const SizedBox(width: 8.0),
|
||||
itemBuilder: (context, index) {
|
||||
final icon =
|
||||
state.availableIconPaths[index];
|
||||
final isSelected =
|
||||
index == state.selectedIconIndex;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(IconStyleSelected(index));
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration:
|
||||
const Duration(milliseconds: 200),
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(18),
|
||||
color: Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
: Colors.transparent,
|
||||
width: 2,
|
||||
.onSurfaceVariant
|
||||
.withAlpha(64)
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHigh,
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
: Colors.transparent,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: CakeImageWidget(
|
||||
imageUrl: icon.path),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: CakeImageWidget(
|
||||
imageUrl: icon.path),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox.shrink(key: ValueKey("icon_style_hidden")),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(16)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).color),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 4, // space between items in a row
|
||||
runSpacing: 8,
|
||||
children: List.generate(
|
||||
state.availableColors.length,
|
||||
(index) => Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(999999999),
|
||||
child: InkWell(
|
||||
borderRadius:
|
||||
BorderRadius.circular(999999999),
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(ColorSelected(index));
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
AnimatedOpacity(
|
||||
duration: const Duration(
|
||||
milliseconds: 200),
|
||||
opacity: index ==
|
||||
state.selectedColorIndex
|
||||
? 1
|
||||
: 0,
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
99999999),
|
||||
border: Border.all(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface))),
|
||||
),
|
||||
AnimatedScale(
|
||||
duration: const Duration(
|
||||
milliseconds: 200),
|
||||
scale: index ==
|
||||
state.selectedColorIndex
|
||||
? 0.8
|
||||
: 1,
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
99999999,
|
||||
),
|
||||
gradient: state
|
||||
.availableColors[index]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox.shrink(key: ValueKey('icon_style_hidden')),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(16)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).color),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 4, // space between items in a row
|
||||
runSpacing: 8,
|
||||
children: List.generate(state.availableColors.length,
|
||||
(index) {
|
||||
return Material(
|
||||
borderRadius: BorderRadius.circular(999999999),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(999999999),
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(ColorSelected(index));
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
AnimatedOpacity(
|
||||
duration: Duration(milliseconds: 200),
|
||||
opacity: index == state.selectedColorIndex
|
||||
? 1
|
||||
: 0,
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(99999999),
|
||||
border: Border.all(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface))),
|
||||
),
|
||||
AnimatedScale(
|
||||
duration: Duration(milliseconds: 200),
|
||||
scale: index == state.selectedColorIndex
|
||||
? 0.8
|
||||
: 1,
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(99999999),
|
||||
gradient:
|
||||
state.availableColors[index]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
)),
|
||||
],
|
||||
)),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
+205
-235
@@ -1,35 +1,40 @@
|
||||
import "dart:async";
|
||||
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import "package:cake_wallet/bitcoin/bitcoin.dart";
|
||||
import "package:cake_wallet/core/auth_service.dart";
|
||||
import "package:cake_wallet/di.dart";
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/main.dart";
|
||||
import 'package:cake_wallet/new-ui/modal_navigator.dart';
|
||||
import 'package:cake_wallet/new-ui/pages/account_customizer.dart';
|
||||
import 'package:cake_wallet/new-ui/pages/card_customizer.dart';
|
||||
import "package:cake_wallet/new-ui/modal_navigator.dart";
|
||||
import "package:cake_wallet/new-ui/pages/card_customizer.dart";
|
||||
import "package:cake_wallet/new-ui/pages/seed/seed_backup_reminder_page.dart";
|
||||
import 'package:cake_wallet/new-ui/pages/settings_page.dart';
|
||||
import 'package:cake_wallet/new-ui/viewmodels/card_customizer/card_customizer_bloc.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/action_row/coin_action_row.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/assets_history_section.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/cards/cards_view.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/mweb_ad.dart';
|
||||
import "package:cake_wallet/new-ui/pages/send_page.dart";
|
||||
import "package:cake_wallet/new-ui/pages/settings_page.dart";
|
||||
import "package:cake_wallet/new-ui/pages/wallet_accounts_page.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/action_row/coin_action_row.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/assets_history/assets_history_section.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/cards/balance_card.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/cards/cards_view.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/mweb_ad.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/seed_backup_reminder_card.dart";
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/top_bar_widget/top_bar.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/unconfirmed_balance_widget.dart';
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/top_bar_widget/top_bar.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/unconfirmed_balance_widget.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/zcash_migration_modal.dart";
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import "package:cake_wallet/routes.dart";
|
||||
import "package:cake_wallet/utils/payment_request.dart";
|
||||
import "package:cake_wallet/view_model/dashboard/dashboard_view_model.dart";
|
||||
import "package:cake_wallet/view_model/dashboard/nft_view_model.dart";
|
||||
import "package:cw_core/unspent_coin_type.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
import "package:flutter/cupertino.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_mobx/flutter_mobx.dart";
|
||||
import "package:mobx/mobx.dart";
|
||||
import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
|
||||
|
||||
class NewHomePage extends StatefulWidget {
|
||||
NewHomePage({super.key, required this.dashboardViewModel, required this.nftViewModel});
|
||||
const NewHomePage({required this.dashboardViewModel, required this.nftViewModel, super.key});
|
||||
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
final NFTViewModel nftViewModel;
|
||||
@@ -39,20 +44,15 @@ class NewHomePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _NewHomePageState extends State<NewHomePage> with RouteAware {
|
||||
MoneroAccountListViewModel? accountListViewModel;
|
||||
bool _lightningMode = false;
|
||||
late final ReactionDisposer _walletReaction;
|
||||
late final ReactionDisposer _migrationReaction;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_setAccountViewModel();
|
||||
|
||||
_walletReaction = reaction((_) => widget.dashboardViewModel.wallet, (_) {
|
||||
_setAccountViewModel();
|
||||
setState(() {
|
||||
_lightningMode = false;
|
||||
});
|
||||
setState(() {});
|
||||
});
|
||||
|
||||
_migrationReaction = reaction((_) => widget.dashboardViewModel.isMigratingToIronwood, (val) {
|
||||
@@ -91,178 +91,183 @@ class _NewHomePageState extends State<NewHomePage> with RouteAware {
|
||||
@override
|
||||
void didPopNext() => widget.dashboardViewModel.loadSeedBackupReminder();
|
||||
|
||||
void _setAccountViewModel() {
|
||||
accountListViewModel = widget.dashboardViewModel.balanceViewModel.hasAccounts
|
||||
? getIt.get<MoneroAccountListViewModel>()
|
||||
: null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).colorScheme.surface,
|
||||
Theme.of(context).colorScheme.surfaceDim,
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
Widget build(BuildContext context) => Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).colorScheme.surface,
|
||||
Theme.of(context).colorScheme.surfaceDim,
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
CustomScrollView(
|
||||
physics: BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
||||
sliver: CupertinoSliverRefreshControl(
|
||||
refreshTriggerPullDistance: 160,
|
||||
refreshIndicatorExtent: 90,
|
||||
onRefresh: () {
|
||||
unawaited(widget.nftViewModel.getNFTAssetByWallet());
|
||||
child: Stack(
|
||||
children: [
|
||||
CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
||||
sliver: CupertinoSliverRefreshControl(
|
||||
refreshTriggerPullDistance: 160,
|
||||
refreshIndicatorExtent: 90,
|
||||
onRefresh: () {
|
||||
unawaited(widget.nftViewModel.getNFTAssetByWallet());
|
||||
|
||||
return widget.dashboardViewModel.refreshDashboard();
|
||||
},
|
||||
return widget.dashboardViewModel.refreshDashboard();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
spacing: 24.0,
|
||||
children: [
|
||||
TopBar(
|
||||
key: ValueKey(widget.dashboardViewModel.wallet.id),
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
lightningMode: _lightningMode,
|
||||
onLightningSwitchPress: () {
|
||||
setState(() {
|
||||
_lightningMode = !_lightningMode;
|
||||
});
|
||||
},
|
||||
onSettingsButtonPress: () async {
|
||||
await CupertinoScaffold.showCupertinoModalBottomSheet(
|
||||
context: context,
|
||||
barrierColor: Colors.black.withAlpha(85),
|
||||
builder: (context) => FractionallySizedBox(
|
||||
child: Material(
|
||||
child: NewSettingsPage(
|
||||
SliverToBoxAdapter(
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final _lightningMode = widget.dashboardViewModel.lightningMode;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
spacing: 24.0,
|
||||
children: [
|
||||
TopBar(
|
||||
key: ValueKey(widget.dashboardViewModel.wallet.id),
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
authService: getIt.get<AuthService>(),
|
||||
))),
|
||||
);
|
||||
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_setAccountViewModel();
|
||||
await widget.dashboardViewModel.loadCardDesigns();
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
),
|
||||
Column(
|
||||
spacing: 20,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => CardsView(
|
||||
key: ValueKey(widget.dashboardViewModel.wallet.name),
|
||||
onCustomizeTapped: openCardCustomizer,
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
accountListViewModel: accountListViewModel,
|
||||
onCompactModeBackgroundCardsTapped: openAccountCustomizer,
|
||||
lightningMode: _lightningMode,
|
||||
onLightningSwitchPress: () {
|
||||
widget.dashboardViewModel.toggleLightningMode();
|
||||
},
|
||||
onSettingsButtonPress: () {
|
||||
CupertinoScaffold.showCupertinoModalBottomSheet(
|
||||
context: context,
|
||||
barrierColor: Colors.black.withAlpha(85),
|
||||
builder: (context) => FractionallySizedBox(
|
||||
child: Material(
|
||||
child: NewSettingsPage(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
authService: getIt.get<AuthService>(),
|
||||
),
|
||||
),
|
||||
),
|
||||
).then((_) async {
|
||||
widget.dashboardViewModel.accountListViewModel?.reload();
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
await widget.dashboardViewModel.loadCardDesigns();
|
||||
if (mounted) setState(() {});
|
||||
});
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
switchInCurve: Curves.easeOut,
|
||||
switchOutCurve: Curves.easeOut,
|
||||
layoutBuilder: (currentChild, previousChildren) => Stack(
|
||||
// the front (selected) card sits at the bottom of the stack box
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: <Widget>[
|
||||
...previousChildren,
|
||||
if (currentChild != null) currentChild,
|
||||
],
|
||||
),
|
||||
child: KeyedSubtree(
|
||||
key: ValueKey(_lightningMode),
|
||||
child: CardsView(
|
||||
key: ValueKey(
|
||||
"${widget.dashboardViewModel.wallet.name}_${_lightningMode}_${widget.dashboardViewModel.accountListViewModel?.accounts.length ?? 0}_${widget.dashboardViewModel.cardDesigns.length}"),
|
||||
onCustomizeTapped: openCardCustomizer,
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
accountListViewModel:
|
||||
widget.dashboardViewModel.accountListViewModel,
|
||||
onCompactModeBackgroundCardsTapped: openAccountCustomizer,
|
||||
lightningMode: _lightningMode),
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(builder: (_) {
|
||||
return AnimatedSize(
|
||||
duration: Duration(milliseconds: 150),
|
||||
curve: Curves.easeInOutCubic,
|
||||
child: (widget.dashboardViewModel.shouldShowBalanceHiddenMessage)
|
||||
? Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 12,
|
||||
width: double.infinity,
|
||||
),
|
||||
Text(
|
||||
S.of(context).long_press_show_balance,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant),
|
||||
)
|
||||
],
|
||||
)
|
||||
: SizedBox(width: double.infinity),
|
||||
);
|
||||
}),
|
||||
UnconfirmedBalanceWidget(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
),
|
||||
],
|
||||
),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
return Column(
|
||||
children: [
|
||||
CoinActionRow(
|
||||
lightningMode: _lightningMode,
|
||||
showSwap: widget.dashboardViewModel.isEnabledSwapAction,
|
||||
walletType: widget.dashboardViewModel.wallet.type,
|
||||
),
|
||||
MwebAd(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
),
|
||||
SeedBackupReminderCard(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
onTap: openSeedBackupReminder,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
Observer(
|
||||
builder: (_) => AnimatedSize(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
curve: Curves.easeInOutCubic,
|
||||
child: (widget.dashboardViewModel
|
||||
.shouldShowBalanceHiddenMessage)
|
||||
? Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
width: double.infinity,
|
||||
),
|
||||
Text(
|
||||
S.of(context).long_press_show_balance,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant),
|
||||
)
|
||||
],
|
||||
)
|
||||
: const SizedBox(width: double.infinity),
|
||||
)),
|
||||
UnconfirmedBalanceWidget(
|
||||
dashboardViewModel: widget.dashboardViewModel),
|
||||
SeedBackupReminderCard(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
onTap: openSeedBackupReminder,
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
CoinActionRow(
|
||||
lightningMode: _lightningMode,
|
||||
showSwap: widget.dashboardViewModel.isEnabledSwapAction,
|
||||
walletType: widget.dashboardViewModel.wallet.type,
|
||||
),
|
||||
MwebAd(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => AssetsHistorySection(
|
||||
nftViewModel: widget.nftViewModel,
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
Observer(
|
||||
builder: (_) => AssetsHistorySection(
|
||||
nftViewModel: widget.nftViewModel,
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
),
|
||||
),
|
||||
const SliverToBoxAdapter(
|
||||
child: SizedBox(height: 80.0),
|
||||
)
|
||||
]),
|
||||
Container(
|
||||
height: (MediaQuery.of(context).padding.top),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
colors: <Color>[
|
||||
Theme.of(context).colorScheme.surface.withAlpha(5),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(25),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(50),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(100),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(150),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(175),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(200),
|
||||
],
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(height: 80.0),
|
||||
)
|
||||
]),
|
||||
Container(
|
||||
height: (MediaQuery.of(context).padding.top),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
colors: <Color>[
|
||||
Theme.of(context).colorScheme.surface.withAlpha(5),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(25),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(50),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(100),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(150),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(175),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(200),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Future<void> openCardCustomizer() async {
|
||||
await CardCustomizer.show(
|
||||
context: context,
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
lightningMode: widget.dashboardViewModel.lightningMode,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -278,54 +283,19 @@ class _NewHomePageState extends State<NewHomePage> with RouteAware {
|
||||
}
|
||||
|
||||
Future<void> openAccountCustomizer() async {
|
||||
final accountList = accountListViewModel;
|
||||
if (accountList == null) {
|
||||
return;
|
||||
}
|
||||
if (widget.dashboardViewModel.accountListViewModel == null) return;
|
||||
|
||||
await CupertinoScaffold.showCupertinoModalBottomSheet(
|
||||
barrierColor: Colors.black.withAlpha(60),
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return ModalNavigator(
|
||||
parentContext: context,
|
||||
heightMode: ModalHeightModes.fullScreen,
|
||||
rootPage: Material(
|
||||
child: AccountCustomizer(
|
||||
accountListViewModel: accountList,
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
builder: (context) => ModalNavigator(
|
||||
parentContext: context,
|
||||
heightMode: ModalHeightModes.fullScreen,
|
||||
rootPage: Material(
|
||||
child: getIt.get<WalletAccountsPage>(param1: widget.dashboardViewModel),
|
||||
),
|
||||
),
|
||||
);
|
||||
await widget.dashboardViewModel.loadCardDesigns();
|
||||
}
|
||||
|
||||
void openCardCustomizer() async {
|
||||
final bloc = getIt.get<CardCustomizerBloc>(
|
||||
param1: _lightningMode,
|
||||
param2: widget.dashboardViewModel.settingsStore.displayAmountsInSatoshi);
|
||||
await CupertinoScaffold.showCupertinoModalBottomSheet(
|
||||
barrierColor: Colors.black.withAlpha(60),
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return ModalNavigator(
|
||||
parentContext: context,
|
||||
heightMode: ModalHeightModes.fullScreen,
|
||||
rootPage: BlocProvider(
|
||||
create: (context) => bloc,
|
||||
child: Material(
|
||||
child: CardCustomizer(
|
||||
cryptoTitle: widget.dashboardViewModel.wallet.currency.fullName ??
|
||||
widget.dashboardViewModel.wallet.currency.name,
|
||||
cryptoName: widget.dashboardViewModel.wallet.currency.name,
|
||||
)),
|
||||
));
|
||||
},
|
||||
);
|
||||
bloc.add(DesignSaved());
|
||||
await bloc.stream.firstWhere((s) => s is CardCustomizerSaved);
|
||||
widget.dashboardViewModel.loadCardDesigns();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ bool _hasMweb(DashboardViewModel vm) => vm.hasMweb;
|
||||
|
||||
bool _hasWalletConnect(DashboardViewModel vm) => vm.hasWalletConnect;
|
||||
|
||||
bool _hasAccounts(DashboardViewModel vm) => vm.balanceViewModel.hasAccounts;
|
||||
|
||||
bool _requiresKeyImageSync(DashboardViewModel vm) =>
|
||||
vm.wallet.type == WalletType.monero &&
|
||||
[HardwareWalletType.cupcake, HardwareWalletType.trezor].contains(vm.wallet.hardwareWalletType);
|
||||
@@ -65,8 +63,8 @@ class SettingsSectionData {
|
||||
SettingsListItem(
|
||||
"assets/new-ui/settings_row_icons/accounts.svg",
|
||||
S.current.accounts,
|
||||
Routes.accountCustomizer,
|
||||
condition: _hasAccounts,
|
||||
Routes.walletAccountsPage,
|
||||
condition: (vm) => vm.wallet.hasAccountsSupport,
|
||||
routeArgsBuilder: (vm) => vm,
|
||||
),
|
||||
SettingsListItem(
|
||||
|
||||
@@ -0,0 +1,567 @@
|
||||
import "dart:async";
|
||||
import "dart:ui";
|
||||
|
||||
import "package:cake_wallet/di.dart";
|
||||
import "package:cake_wallet/entities/bitcoin_amount_display_mode.dart";
|
||||
import "package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart";
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/new-ui/pages/card_customizer.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/coins_page/cards/balance_card.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
|
||||
import "package:cake_wallet/src/screens/settings/widgets/account_creation_modal.dart";
|
||||
import "package:cake_wallet/src/widgets/alert_with_one_action.dart";
|
||||
import "package:cake_wallet/src/widgets/alert_with_two_actions.dart";
|
||||
import "package:cake_wallet/src/widgets/new_list_row/new_list_section.dart";
|
||||
import "package:cake_wallet/utils/show_pop_up.dart";
|
||||
import "package:cake_wallet/view_model/dashboard/dashboard_view_model.dart";
|
||||
import "package:cake_wallet/view_model/wallet_account_list/account_edit_or_create_view_model.dart";
|
||||
import "package:cake_wallet/view_model/wallet_account_list/account_list_item.dart";
|
||||
import "package:cake_wallet/view_model/wallet_account_list/wallet_account_list_view_model.dart";
|
||||
import "package:cw_core/balance_card_style_settings.dart";
|
||||
import "package:cw_core/card_design.dart";
|
||||
import "package:cw_core/sync_status.dart";
|
||||
import "package:cw_core/utils/print_verbose.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:mobx/mobx.dart";
|
||||
import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
|
||||
|
||||
class AccountCustomizerListItem {
|
||||
AccountCustomizerListItem({
|
||||
required this.card,
|
||||
required this.order,
|
||||
required this.accountListItem,
|
||||
});
|
||||
|
||||
final BalanceCard card;
|
||||
final int order;
|
||||
final AccountListItem accountListItem;
|
||||
}
|
||||
|
||||
class WalletAccountsPage extends StatefulWidget {
|
||||
const WalletAccountsPage({
|
||||
required this.accountListViewModel,
|
||||
required this.accountEditOrCreateViewModel,
|
||||
required this.dashboardViewModel,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final WalletAccountListViewModel accountListViewModel;
|
||||
final WalletAccountEditOrCreateViewModel accountEditOrCreateViewModel;
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
|
||||
@override
|
||||
State<WalletAccountsPage> createState() => _WalletAccountsPageState();
|
||||
}
|
||||
|
||||
class _WalletAccountsPageState extends State<WalletAccountsPage> {
|
||||
static const double _kStackVisibleFactor = 0.2;
|
||||
late final double cardWidth = MediaQuery.of(context).size.width * 0.9;
|
||||
|
||||
final List<AccountCustomizerListItem> _items = [];
|
||||
|
||||
ReactionDisposer? _accountsReaction;
|
||||
|
||||
WalletAccountListViewModel get accountListViewModel =>
|
||||
widget.dashboardViewModel.accountListViewModel ?? widget.accountListViewModel;
|
||||
|
||||
bool get _isMultiAccountsEnabled =>
|
||||
widget.dashboardViewModel.isMultiAccountsEnabled;
|
||||
|
||||
String get _assetName => widget.dashboardViewModel.appStore.amountParsingProxy
|
||||
.getCryptoSymbol(accountListViewModel.currency);
|
||||
|
||||
|
||||
bool get _capitalizeAssetName =>
|
||||
widget.dashboardViewModel.wallet.type != WalletType.bitcoin ||
|
||||
widget.dashboardViewModel.settingsStore.displayAmountsInSatoshi !=
|
||||
BitcoinAmountDisplayMode.satoshi;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_accountsReaction = reaction(
|
||||
(_) => accountListViewModel.accounts
|
||||
.map((account) => "${account.id}:${account.label}:${account.balance}")
|
||||
.join(","),
|
||||
(_) {
|
||||
if (!mounted) return;
|
||||
loadCards();
|
||||
},
|
||||
);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await widget.dashboardViewModel.loadCardDesigns();
|
||||
if (!mounted) return;
|
||||
loadCards();
|
||||
|
||||
if (!_isMultiAccountsEnabled) return;
|
||||
|
||||
final activeId = accountListViewModel.selectedAccount?.id;
|
||||
|
||||
for (int i = 0; i < _items.length - 1; i++) {
|
||||
if (activeId != null && _items[i].accountListItem.id == activeId) {
|
||||
final lastIndex = _items.length - 1;
|
||||
final temp = _items[i];
|
||||
_items[i] = _items[lastIndex];
|
||||
_items[lastIndex] = temp;
|
||||
|
||||
await saveCardOrder();
|
||||
await widget.dashboardViewModel.loadCardDesigns();
|
||||
loadCards();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_accountsReaction?.call();
|
||||
accountListViewModel.reload();
|
||||
saveCardOrder().then((value) => widget.dashboardViewModel.loadCardDesigns());
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void loadCards() {
|
||||
final allAccounts = accountListViewModel.accounts;
|
||||
final visible = widget.dashboardViewModel.visibleAccounts;
|
||||
|
||||
if (visible.isEmpty && _items.isNotEmpty) return;
|
||||
|
||||
_items.clear();
|
||||
|
||||
if (!_isMultiAccountsEnabled) {
|
||||
if (visible.isNotEmpty) {
|
||||
final account = visible.first;
|
||||
final realIndex = allAccounts.indexWhere((a) => a.id == account.id);
|
||||
final design = (realIndex >= 0 &&
|
||||
realIndex < widget.dashboardViewModel.cardDesigns.length)
|
||||
? widget.dashboardViewModel.cardDesigns[realIndex]
|
||||
: CardDesign.genericDefault;
|
||||
|
||||
_items.add(AccountCustomizerListItem(
|
||||
card: BalanceCard(
|
||||
accountName: account.label,
|
||||
accountIndex: account.id,
|
||||
balance: account.balance ?? "0.00",
|
||||
accountBalance: account.balance ?? "0.00",
|
||||
designSwitchDuration: Duration.zero,
|
||||
assetName: _assetName,
|
||||
capitalizeAssetName: _capitalizeAssetName,
|
||||
selected: true,
|
||||
width: cardWidth,
|
||||
design: design,
|
||||
),
|
||||
order: realIndex >= 0 ? realIndex : 0,
|
||||
accountListItem: account,
|
||||
));
|
||||
}
|
||||
if (mounted) setState(() {});
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < allAccounts.length; i++) {
|
||||
final realIndex = widget.dashboardViewModel.cardOrder[i];
|
||||
|
||||
if (realIndex == null || realIndex < 0 || realIndex >= allAccounts.length) {
|
||||
// db order broken.
|
||||
reset();
|
||||
break;
|
||||
}
|
||||
|
||||
_items.add(AccountCustomizerListItem(
|
||||
card: BalanceCard(
|
||||
accountName: allAccounts[realIndex].label,
|
||||
accountIndex: allAccounts[realIndex].id,
|
||||
balance: allAccounts[realIndex].balance ?? "0.00",
|
||||
accountBalance: allAccounts[realIndex].balance ?? "0.00",
|
||||
designSwitchDuration: Duration.zero,
|
||||
assetName: _assetName,
|
||||
capitalizeAssetName: _capitalizeAssetName,
|
||||
selected: i == allAccounts.length - 1,
|
||||
width: cardWidth,
|
||||
design: widget.dashboardViewModel.cardDesigns[realIndex],
|
||||
),
|
||||
order: realIndex,
|
||||
accountListItem: allAccounts[realIndex]));
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final showResetButton = _isMultiAccountsEnabled && _items.length > 1;
|
||||
final showAccountsToggle = widget.dashboardViewModel.canToggleMultiAccounts;
|
||||
final isToggleEnabled = widget.dashboardViewModel.isMultiAccountsEnabled;
|
||||
|
||||
if (_items.isEmpty && !showAccountsToggle) return const SizedBox.shrink();
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(24))),
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: S.of(context).wallet_accounts,
|
||||
leadingIcon: const Icon(Icons.close),
|
||||
leadingSemanticLabel: S.of(context).close,
|
||||
onLeadingPressed: Navigator.of(context).maybePop,
|
||||
trailingIcon: showResetButton ? const Icon(Icons.refresh) : null,
|
||||
trailingSemanticLabel: S.of(context).reset,
|
||||
onTrailingPressed: showResetDialog,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
if (showAccountsToggle)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: NewListSections(
|
||||
sections: {
|
||||
"": [
|
||||
ListItemToggle(
|
||||
keyValue: S.of(context).multiple_accounts,
|
||||
label: S.of(context).multiple_accounts,
|
||||
value: isToggleEnabled,
|
||||
onChanged: (val) async {
|
||||
await widget.dashboardViewModel.setMultiAccountsEnabled(val);
|
||||
if (!mounted) return;
|
||||
loadCards();
|
||||
}),
|
||||
],
|
||||
},
|
||||
),
|
||||
),
|
||||
if (_isMultiAccountsEnabled && _items.isNotEmpty)
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 24.0),
|
||||
child: Text(
|
||||
S.of(context).account_customizer_desc,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
ReorderableListView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 196),
|
||||
scrollController: ModalScrollController.of(context),
|
||||
onReorder: reorder,
|
||||
proxyDecorator: (child, index, animation) => AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (context, _) {
|
||||
final animValue = Curves.easeOutCubic.transform(animation.value);
|
||||
final scale = lerpDouble(1, 1.05, animValue)!;
|
||||
|
||||
return Opacity(
|
||||
opacity: 1 - animValue.clamp(0.0, 0.1),
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: cardWidth,
|
||||
child: Transform.scale(
|
||||
scale: scale,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: _items[index].card,
|
||||
),
|
||||
itemCount: _items.length,
|
||||
itemBuilder: (context, int index) {
|
||||
final card = _items[index].card;
|
||||
final selectedItemIndex = _items.length - 1;
|
||||
|
||||
return Container(
|
||||
key: ValueKey(index),
|
||||
child: Semantics(
|
||||
button: true,
|
||||
selected: selectedItemIndex == index,
|
||||
label: _items[index].accountListItem.label,
|
||||
onTap: () => reorder(index, _items.length),
|
||||
child: GestureDetector(
|
||||
excludeFromSemantics: true,
|
||||
onTap: () => reorder(index, _items.length),
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
heightFactor: _kStackVisibleFactor,
|
||||
child: card,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: MergeSemantics(
|
||||
child: Semantics(
|
||||
button: true,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(999999),
|
||||
onTap: _showAddAccountModal,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(999999),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 18),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add,
|
||||
size: 28,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
Text(
|
||||
S.of(context).add_account,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool _checkReadyToManage() {
|
||||
if (widget.dashboardViewModel.wallet.type == WalletType.bitcoin) {
|
||||
return true;
|
||||
}
|
||||
if (widget.dashboardViewModel.status is! SyncedSyncStatus) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).wallet_is_syncing,
|
||||
alertContent: S.of(context).cannot_manage_accounts_during_sync,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: Navigator.of(context).pop,
|
||||
),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> _showAddAccountModal() async {
|
||||
if (!_checkReadyToManage()) return;
|
||||
|
||||
final createViewModel = getIt.get<WalletAccountEditOrCreateViewModel>();
|
||||
|
||||
final res = await showCupertinoModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => Material(
|
||||
child: AccountCreationModal(
|
||||
viewModel: createViewModel,
|
||||
),
|
||||
));
|
||||
|
||||
if (res != null && res is bool && res == true) {
|
||||
await accountListViewModel.reload();
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
final accounts = accountListViewModel.accounts;
|
||||
if (accounts.isNotEmpty) {
|
||||
final newestAccount = accounts.reduce((a, b) => a.id > b.id ? a : b);
|
||||
await accountListViewModel.select(newestAccount);
|
||||
}
|
||||
|
||||
await widget.dashboardViewModel.loadCardDesigns();
|
||||
loadCards();
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _openCardCustomizer() async {
|
||||
if (!_checkReadyToManage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await CardCustomizer.show(
|
||||
context: context,
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
lightningMode: widget.dashboardViewModel.lightningMode,
|
||||
asModalSheet: false,
|
||||
onSaved: _reloadAccountsAndCards,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _reloadAccountsAndCards() async {
|
||||
await accountListViewModel.reload();
|
||||
await widget.dashboardViewModel.loadCardDesigns();
|
||||
if (!mounted) return;
|
||||
loadCards();
|
||||
}
|
||||
|
||||
void _onCardsReordered(int oldRealIndex, int newRealIndex) {
|
||||
final accounts = accountListViewModel.accounts;
|
||||
if (oldRealIndex < 0 ||
|
||||
newRealIndex < 0 ||
|
||||
oldRealIndex >= accounts.length ||
|
||||
newRealIndex >= accounts.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
final oldAccountId = accounts[oldRealIndex].id;
|
||||
final newAccountId = accounts[newRealIndex].id;
|
||||
|
||||
final from = _items.indexWhere((item) => item.accountListItem.id == oldAccountId);
|
||||
final to = _items.indexWhere((item) => item.accountListItem.id == newAccountId);
|
||||
if (from < 0 || to < 0 || from == to) return;
|
||||
|
||||
final insertIndex = from < to ? to + 1 : to;
|
||||
reorder(from, insertIndex);
|
||||
|
||||
saveCardOrder().then((_) {
|
||||
if (!mounted) return;
|
||||
widget.dashboardViewModel.loadCardDesigns();
|
||||
});
|
||||
}
|
||||
|
||||
void reorder(int oldIndex, int newIndex) {
|
||||
if (!_isMultiAccountsEnabled) return;
|
||||
setState(() {
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final AccountCustomizerListItem item = _items.removeAt(oldIndex);
|
||||
_items.insert(newIndex, item);
|
||||
});
|
||||
|
||||
// necessary to copy all this to keep constant constructor for BalanceCard
|
||||
for (int i = 0; i < _items.length; i++) {
|
||||
_items[i] = AccountCustomizerListItem(
|
||||
card: BalanceCard(
|
||||
accountName: _items[i].card.accountName,
|
||||
balance: _items[i].card.balance,
|
||||
accountIndex: _items[i].card.accountIndex,
|
||||
accountBalance: _items[i].card.accountBalance,
|
||||
assetName: _items[i].card.assetName,
|
||||
capitalizeAssetName: _items[i].card.capitalizeAssetName,
|
||||
designSwitchDuration: _items[i].card.designSwitchDuration,
|
||||
onCustomizeTapped: (i == _items.length - 1) ? _openCardCustomizer : null,
|
||||
selected: i == _items.length - 1,
|
||||
width: _items[i].card.width,
|
||||
design: _items[i].card.design,
|
||||
),
|
||||
order: i,
|
||||
accountListItem: _items[i].accountListItem);
|
||||
}
|
||||
|
||||
if (newIndex == _items.length - 1 || oldIndex == _items.length - 1) {
|
||||
accountListViewModel.select(_items[_items.length - 1].accountListItem);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveCardOrder() async {
|
||||
|
||||
if (!_isMultiAccountsEnabled) return;
|
||||
|
||||
for (int orderIndex = 0; orderIndex < _items.length; orderIndex++) {
|
||||
final item = _items[orderIndex];
|
||||
printV("${item.accountListItem.id}: $orderIndex");
|
||||
|
||||
final existing = await BalanceCardStyleSettings.get(
|
||||
widget.dashboardViewModel.wallet.walletInfo.internalId,
|
||||
item.accountListItem.id,
|
||||
);
|
||||
|
||||
await BalanceCardStyleSettings.fromCardDesign(
|
||||
walletInfoId: widget.dashboardViewModel.wallet.walletInfo.internalId,
|
||||
accountIndex: item.accountListItem.id,
|
||||
cardOrder: orderIndex,
|
||||
design: item.card.design,
|
||||
iconStyleIndex: existing?.iconStyleIndex ?? 0,
|
||||
gradientIndexOverride: existing?.gradientIndex)
|
||||
.insert();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showResetDialog() async {
|
||||
final res = await showPopUp(
|
||||
context: context,
|
||||
builder: (context) => AlertWithTwoActions(
|
||||
alertTitle: S.of(context).reset,
|
||||
alertContent: S.of(context).card_order_reset_desc,
|
||||
leftButtonText: S.of(context).yes,
|
||||
rightButtonText: S.of(context).no,
|
||||
actionLeftButton: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
actionRightButton: Navigator.of(context).pop));
|
||||
if (res != null && res is bool && res) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reset() async {
|
||||
_items.clear();
|
||||
|
||||
final accounts = accountListViewModel.accounts;
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
_items.add(AccountCustomizerListItem(
|
||||
card: BalanceCard(
|
||||
accountName: accounts[i].label,
|
||||
accountIndex: accounts[i].id,
|
||||
balance: accounts[i].balance ?? "0.00",
|
||||
accountBalance: accounts[i].balance ?? "0.00",
|
||||
assetName: _assetName,
|
||||
capitalizeAssetName: _capitalizeAssetName,
|
||||
selected: i == accounts.length - 1,
|
||||
designSwitchDuration: const Duration(milliseconds: 200),
|
||||
width: cardWidth,
|
||||
design: i >= widget.dashboardViewModel.cardDesigns.length
|
||||
? CardDesign.genericDefault
|
||||
: widget.dashboardViewModel.cardDesigns[i],
|
||||
),
|
||||
order: i,
|
||||
accountListItem: accounts[i]));
|
||||
}
|
||||
|
||||
await saveCardOrder();
|
||||
|
||||
if (accounts.isNotEmpty) {
|
||||
await accountListViewModel.select(accounts.last);
|
||||
}
|
||||
|
||||
await widget.dashboardViewModel.loadCardDesigns();
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import "package:cake_wallet/bitcoin/bitcoin.dart";
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import "package:cw_core/balance_card_style_settings.dart";
|
||||
@@ -30,6 +31,9 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
add(_Init());
|
||||
}
|
||||
|
||||
bool get _accountNameEnabled =>
|
||||
!lightningMode && _wallet.walletInfo.isMultiAccountsEnabled == true;
|
||||
|
||||
List<Gradient> _updateAvailableColors(CardDesign currentDesign) {
|
||||
final list = List<Gradient>.from(CardDesign.allGradients, growable: true);
|
||||
if (CardDesign.specialDesignsForCurrencies[_wallet.currency] != null) {
|
||||
@@ -83,23 +87,9 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
}
|
||||
|
||||
void _init(_Init event, Emitter<CardCustomizerState> emit) async {
|
||||
late final account;
|
||||
if (_wallet.type == WalletType.monero) {
|
||||
account = monero!.getCurrentAccount(_wallet);
|
||||
} else if (_wallet.type == WalletType.wownero) {
|
||||
account = wownero!.getCurrentAccount(_wallet);
|
||||
} else {
|
||||
account = null;
|
||||
}
|
||||
final accountName = (account?.label ?? "") as String;
|
||||
late final int accountIndex;
|
||||
if (account != null) {
|
||||
accountIndex = account.id as int;
|
||||
} else if (lightningMode) {
|
||||
accountIndex = 0;
|
||||
} else {
|
||||
accountIndex = -1;
|
||||
}
|
||||
final accountInfo = await _getCurrentAccountInfo();
|
||||
final accountName = accountInfo.accountName;
|
||||
final accountIndex = accountInfo.accountIndex;
|
||||
final curr = lightningMode ? CryptoCurrency.btcln : _wallet.currency;
|
||||
final currentDesignSettings = await _loadCurrentDesignSettings(accountIndex);
|
||||
final currentDesign = CardDesign.fromStyleSettings(currentDesignSettings, curr);
|
||||
@@ -123,6 +113,39 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
selectedIconIndex: selectedIconIndex));
|
||||
}
|
||||
|
||||
Future<({String accountName, int accountIndex})> _getCurrentAccountInfo() async {
|
||||
if (lightningMode) {
|
||||
return (accountName: "", accountIndex: -2);
|
||||
}
|
||||
|
||||
final account = await _resolveCurrentAccount();
|
||||
|
||||
if (!_accountNameEnabled) {
|
||||
return (accountName: "", accountIndex: account.accountIndex);
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
Future<({String accountName, int accountIndex})> _resolveCurrentAccount() async {
|
||||
if (_wallet.type == WalletType.monero) {
|
||||
final account = monero!.getCurrentAccount(_wallet);
|
||||
return (accountName: account.label, accountIndex: account.id);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.wownero) {
|
||||
final account = wownero!.getCurrentAccount(_wallet);
|
||||
return (accountName: account.label, accountIndex: account.id);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.bitcoin) {
|
||||
final account = await bitcoin!.getCurrentAccount(_wallet);
|
||||
return (accountName: account.label, accountIndex: account.id);
|
||||
}
|
||||
|
||||
return (accountName: "", accountIndex: -1);
|
||||
}
|
||||
|
||||
void _onDesignSelected(CardDesignSelected event, Emitter<CardCustomizerState> emit) {
|
||||
final newColors = _updateAvailableColors(state.availableDesigns[event.newDesignIndex]);
|
||||
late final int newColorIndex;
|
||||
@@ -152,32 +175,34 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
emit(state.copyWith(accountName: event.newAccountName));
|
||||
}
|
||||
|
||||
void _onDesignSaved(DesignSaved event, Emitter<CardCustomizerState> emit) {
|
||||
BalanceCardStyleSettings.fromCardDesign(
|
||||
Future<void> _onDesignSaved(DesignSaved event, Emitter<CardCustomizerState> emit) async {
|
||||
await BalanceCardStyleSettings.fromCardDesign(
|
||||
walletInfoId: _wallet.walletInfo.internalId,
|
||||
accountIndex: state.accountIndex,
|
||||
cardOrder: state.cardOrder,
|
||||
design: state.selectedDesign,
|
||||
iconStyleIndex: state.selectedIconIndex,
|
||||
gradientIndexOverride: state.selectedColorIndex)
|
||||
.insert()
|
||||
.then((value) {
|
||||
emit(CardCustomizerSaved(
|
||||
state.selectedDesignIndex,
|
||||
state.selectedColorIndex,
|
||||
state.availableDesigns,
|
||||
state.availableColors,
|
||||
state.accountName,
|
||||
state.accountIndex,
|
||||
state.displaySats,
|
||||
state.cardOrder,
|
||||
availableIconPaths: state.availableIconPaths,
|
||||
selectedIconIndex: state.selectedIconIndex));
|
||||
});
|
||||
saveAccountName();
|
||||
.insert();
|
||||
|
||||
await saveAccountName();
|
||||
|
||||
emit(CardCustomizerSaved(
|
||||
state.selectedDesignIndex,
|
||||
state.selectedColorIndex,
|
||||
state.availableDesigns,
|
||||
state.availableColors,
|
||||
state.accountName,
|
||||
state.accountIndex,
|
||||
state.displaySats,
|
||||
state.cardOrder,
|
||||
availableIconPaths: state.availableIconPaths,
|
||||
selectedIconIndex: state.selectedIconIndex));
|
||||
}
|
||||
|
||||
Future<void> saveAccountName() async {
|
||||
if (!_accountNameEnabled) return;
|
||||
|
||||
if (_wallet.type == WalletType.monero) {
|
||||
await saveMoneroAccountName();
|
||||
}
|
||||
@@ -185,6 +210,9 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
if (_wallet.type == WalletType.wownero) {
|
||||
await saveWowneroAccountName();
|
||||
}
|
||||
if (_wallet.type == WalletType.bitcoin && !lightningMode) {
|
||||
await saveBitcoinAccountName();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveMoneroAccountName() async {
|
||||
@@ -202,4 +230,11 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
|
||||
await _wallet.save();
|
||||
}
|
||||
|
||||
Future<void> saveBitcoinAccountName() async {
|
||||
await _wallet.walletInfo.renameAccount(
|
||||
accountIndex: state.accountIndex,
|
||||
label: state.accountName,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,10 +215,12 @@ class BalanceCard extends StatelessWidget {
|
||||
child: AnimatedSwitcher(
|
||||
duration: designSwitchDuration,
|
||||
layoutBuilder: (currentChild, previousChildren) {
|
||||
final currentKey = currentChild?.key;
|
||||
return Stack(
|
||||
alignment: Alignment.centerLeft,
|
||||
children: <Widget>[
|
||||
...previousChildren,
|
||||
for (final child in previousChildren)
|
||||
if (child.key != currentKey) child,
|
||||
if (currentChild != null) currentChild,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_account_list/wallet_account_list_view_model.dart';
|
||||
import 'package:cw_core/card_design.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
@@ -30,14 +30,14 @@ import 'balance_card.dart';
|
||||
class CardsView extends StatefulWidget {
|
||||
const CardsView(
|
||||
{super.key,
|
||||
required this.dashboardViewModel,
|
||||
required this.accountListViewModel,
|
||||
required this.lightningMode,
|
||||
required this.onCompactModeBackgroundCardsTapped,
|
||||
required this.onCustomizeTapped});
|
||||
required this.dashboardViewModel,
|
||||
required this.accountListViewModel,
|
||||
required this.lightningMode,
|
||||
required this.onCompactModeBackgroundCardsTapped,
|
||||
required this.onCustomizeTapped});
|
||||
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
final MoneroAccountListViewModel? accountListViewModel;
|
||||
final WalletAccountListViewModel? accountListViewModel;
|
||||
final VoidCallback onCompactModeBackgroundCardsTapped;
|
||||
final VoidCallback onCustomizeTapped;
|
||||
final bool lightningMode;
|
||||
@@ -49,22 +49,39 @@ class CardsView extends StatefulWidget {
|
||||
class _CardsViewState extends State<CardsView> {
|
||||
late int _selectedIndex;
|
||||
bool isFirstBuild = true;
|
||||
ReactionDisposer? _cardOrderReaction;
|
||||
|
||||
int get _fallbackSelectedIndex =>
|
||||
max(0, widget.dashboardViewModel.cardOrder.length - 1);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedIndex = widget.dashboardViewModel.cardOrder.length - 1;
|
||||
reaction(
|
||||
(_) => widget.dashboardViewModel.cardOrder.values.toList(),
|
||||
(_) => setState(() {
|
||||
_selectedIndex = widget.dashboardViewModel.cardOrder.length - 1;
|
||||
}));
|
||||
_selectedIndex = _fallbackSelectedIndex;
|
||||
_cardOrderReaction = reaction(
|
||||
(_) => widget.dashboardViewModel.cardOrder.values.toList(),
|
||||
(_) => setState(() {
|
||||
_selectedIndex = _fallbackSelectedIndex;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_cardOrderReaction?.call();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
static const Duration animDuration = Duration(milliseconds: 200);
|
||||
static const int compactModeTreshold = 4;
|
||||
static const int maxCards = 5;
|
||||
|
||||
/// No account name on the card: Lightning card, or Bitcoin with multi-accounts off.
|
||||
bool get _hideAccountInfo =>
|
||||
widget.lightningMode ||
|
||||
(widget.dashboardViewModel.wallet.type == WalletType.bitcoin &&
|
||||
!widget.dashboardViewModel.isMultiAccountsEnabled);
|
||||
|
||||
Widget _buildCard(int visualIndex, int realIndex, int numCards, double parentWidth,
|
||||
Map<int, int> order, bool compactMode, double overlapAmount) {
|
||||
final baseTop = overlapAmount * (numCards - 1);
|
||||
@@ -79,7 +96,7 @@ class _CardsViewState extends State<CardsView> {
|
||||
|
||||
final isSelected = _selectedIndex == visualIndex;
|
||||
final accounts = widget.accountListViewModel?.accounts;
|
||||
final cardLabel = (accounts != null && realIndex < accounts.length)
|
||||
final cardLabel = (!_hideAccountInfo && accounts != null && realIndex < accounts.length)
|
||||
? accounts[realIndex].label
|
||||
: S.of(context).balance;
|
||||
|
||||
@@ -89,7 +106,7 @@ class _CardsViewState extends State<CardsView> {
|
||||
widget.onCompactModeBackgroundCardsTapped();
|
||||
} else if (!compactMode) {
|
||||
setState(() {
|
||||
if (widget.accountListViewModel != null)
|
||||
if (widget.accountListViewModel != null && !widget.lightningMode)
|
||||
widget.accountListViewModel!.select(widget.accountListViewModel!.accounts[realIndex]);
|
||||
_selectedIndex = visualIndex;
|
||||
});
|
||||
@@ -121,9 +138,9 @@ class _CardsViewState extends State<CardsView> {
|
||||
label: cardLabel,
|
||||
hint: isSelected
|
||||
? (widget.dashboardViewModel.balanceViewModel.displayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? S.of(context).long_press_show_balance
|
||||
: S.of(context).long_press_hide_balance)
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? S.of(context).long_press_show_balance
|
||||
: S.of(context).long_press_hide_balance)
|
||||
: null,
|
||||
onTap: onCardTap,
|
||||
onLongPress: isSelected ? onCardLongPress : null,
|
||||
@@ -168,13 +185,14 @@ class _CardsViewState extends State<CardsView> {
|
||||
realIndex >= widget.dashboardViewModel.cardDesigns.length)
|
||||
cardDesign = CardDesign.genericDefault;
|
||||
else if (widget.lightningMode)
|
||||
cardDesign = widget.dashboardViewModel.cardDesigns[realIndex + 1];
|
||||
// the lightning design is always the last one (after all bitcoin accounts)
|
||||
cardDesign = widget.dashboardViewModel.cardDesigns.last;
|
||||
else
|
||||
cardDesign = widget.dashboardViewModel.cardDesigns[realIndex];
|
||||
|
||||
final String accountName;
|
||||
final String accountBalance;
|
||||
if (account == null) {
|
||||
if (account == null || _hideAccountInfo) {
|
||||
accountName = "";
|
||||
accountBalance = "";
|
||||
} else {
|
||||
@@ -188,30 +206,30 @@ class _CardsViewState extends State<CardsView> {
|
||||
|
||||
final List<BalanceCardAction> actions = widget.lightningMode
|
||||
? [
|
||||
BalanceCardAction(
|
||||
label: S.current.bitcoin_lightning_deposit,
|
||||
icon: Icons.arrow_downward,
|
||||
onTap: depositToL2,
|
||||
),
|
||||
BalanceCardAction(
|
||||
label: S.current.bitcoin_lightning_withdraw,
|
||||
icon: Icons.arrow_upward,
|
||||
onTap: withdrawFromL2,
|
||||
)
|
||||
]
|
||||
BalanceCardAction(
|
||||
label: S.current.bitcoin_lightning_deposit,
|
||||
icon: Icons.arrow_downward,
|
||||
onTap: depositToL2,
|
||||
),
|
||||
BalanceCardAction(
|
||||
label: S.current.bitcoin_lightning_withdraw,
|
||||
icon: Icons.arrow_upward,
|
||||
onTap: withdrawFromL2,
|
||||
)
|
||||
]
|
||||
: widget.dashboardViewModel.isEnabledTradeAction
|
||||
? [
|
||||
BalanceCardAction(
|
||||
label: S.current.buy,
|
||||
icon: Icons.arrow_forward_ios_rounded,
|
||||
iconSize: 12,
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context, builder: (context) => BuySellSelectorModal());
|
||||
},
|
||||
)
|
||||
]
|
||||
: [];
|
||||
? [
|
||||
BalanceCardAction(
|
||||
label: S.current.buy,
|
||||
icon: Icons.arrow_forward_ios_rounded,
|
||||
iconSize: 12,
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context, builder: (context) => BuySellSelectorModal());
|
||||
},
|
||||
)
|
||||
]
|
||||
: [];
|
||||
|
||||
return BalanceCard(
|
||||
width: effectiveCardWidth,
|
||||
@@ -263,31 +281,55 @@ class _CardsViewState extends State<CardsView> {
|
||||
|
||||
double _getBoxHeight(int numCards, double overlapAmount) {
|
||||
return
|
||||
/* height of initial card */
|
||||
(2 / 3.2) * (effectiveCardWidth) +
|
||||
/* height of bg card * amount of bg cards */
|
||||
overlapAmount * ((numCards) - 1);
|
||||
/* height of initial card */
|
||||
(2 / 3.2) * (effectiveCardWidth) +
|
||||
/* height of bg card * amount of bg cards */
|
||||
overlapAmount * ((numCards) - 1);
|
||||
}
|
||||
|
||||
|
||||
int _visibleCardsCount() {
|
||||
if (widget.dashboardViewModel.wallet.type != WalletType.bitcoin) {
|
||||
return widget.dashboardViewModel.cardDesigns.length;
|
||||
}
|
||||
|
||||
if (widget.lightningMode || !widget.dashboardViewModel.isMultiAccountsEnabled) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return widget.accountListViewModel?.accounts.length ?? 1;
|
||||
}
|
||||
|
||||
int? _visualIndexForSelectedAccount(Map<int, int> order) {
|
||||
final vm = widget.accountListViewModel;
|
||||
if (vm == null || _hideAccountInfo) return null;
|
||||
|
||||
final selectedId = vm.selectedAccount?.id;
|
||||
final realIndex = vm.accounts.indexWhere((a) => a.id == selectedId);
|
||||
if (realIndex < 0) return null;
|
||||
|
||||
for (final entry in order.entries) {
|
||||
if (entry.value == realIndex) return entry.key;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(builder: (context) {
|
||||
Widget build(BuildContext context) => Observer(builder: (context) {
|
||||
final parentWidth = MediaQuery.of(context).size.width;
|
||||
final children = <Widget>[];
|
||||
|
||||
int numCards = widget.dashboardViewModel.wallet.type == WalletType.bitcoin
|
||||
? 1
|
||||
: widget.dashboardViewModel.cardDesigns.length;
|
||||
int numCards = _visibleCardsCount();
|
||||
if (numCards == 0) numCards = 1;
|
||||
|
||||
if (_selectedIndex >= (numCards)) {
|
||||
if (_selectedIndex >= numCards) {
|
||||
_selectedIndex = 0;
|
||||
}
|
||||
|
||||
Map<int, int> order = widget.dashboardViewModel.cardOrder.length != numCards
|
||||
? Map<int, int>.fromEntries(
|
||||
List.generate(numCards, (i) => MapEntry(i, i)),
|
||||
)
|
||||
List.generate(numCards, (i) => MapEntry(i, i)),
|
||||
)
|
||||
: widget.dashboardViewModel.cardOrder;
|
||||
|
||||
for (int i = min(numCards - 1, maxCards); i >= 0; i--) {
|
||||
@@ -299,6 +341,9 @@ class _CardsViewState extends State<CardsView> {
|
||||
}
|
||||
}
|
||||
|
||||
final followIndex = _visualIndexForSelectedAccount(order);
|
||||
if (followIndex != null) _selectedIndex = followIndex;
|
||||
|
||||
final bool compactMode = numCards >= compactModeTreshold;
|
||||
final double overlapAmount = compactMode ? 5.0 : 46.0;
|
||||
for (int i = min(numCards - 1, maxCards); i >= 0; i--) {
|
||||
@@ -306,14 +351,6 @@ class _CardsViewState extends State<CardsView> {
|
||||
|
||||
int realIndex = order[visualIndex]!;
|
||||
|
||||
if (visualIndex == _selectedIndex &&
|
||||
widget.accountListViewModel != null &&
|
||||
realIndex < widget.accountListViewModel!.accounts.length &&
|
||||
widget.accountListViewModel?.selected.label !=
|
||||
widget.accountListViewModel?.accounts[realIndex].label) {
|
||||
widget.accountListViewModel!.select(widget.accountListViewModel!.accounts[realIndex]);
|
||||
}
|
||||
|
||||
children.add(_buildCard(
|
||||
visualIndex, realIndex, numCards, parentWidth, order, compactMode, overlapAmount));
|
||||
}
|
||||
@@ -331,7 +368,6 @@ class _CardsViewState extends State<CardsView> {
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> depositToL2() async {
|
||||
PaymentRequest? paymentRequest = null;
|
||||
@@ -343,7 +379,7 @@ class _CardsViewState extends State<CardsView> {
|
||||
}
|
||||
} else if (widget.dashboardViewModel.type == WalletType.bitcoin) {
|
||||
final depositAddress =
|
||||
await bitcoin!.getUnusedSpakDepositAddress(widget.dashboardViewModel.wallet);
|
||||
await bitcoin!.getUnusedSpakDepositAddress(widget.dashboardViewModel.wallet);
|
||||
if ((depositAddress?.isNotEmpty ?? false)) {
|
||||
paymentRequest = PaymentRequest.fromUri(Uri.parse("bitcoin:$depositAddress"));
|
||||
}
|
||||
@@ -352,10 +388,10 @@ class _CardsViewState extends State<CardsView> {
|
||||
if (widget.dashboardViewModel.type == WalletType.bitcoin) {
|
||||
final page = getIt.get<NewSendPage>(
|
||||
param1: SendPageParams(
|
||||
initialPaymentRequest: paymentRequest,
|
||||
unspentCoinType: UnspentCoinType.nonMweb,
|
||||
mode: SendPageModes.lightningDeposit,
|
||||
));
|
||||
initialPaymentRequest: paymentRequest,
|
||||
unspentCoinType: UnspentCoinType.nonMweb,
|
||||
mode: SendPageModes.lightningDeposit,
|
||||
));
|
||||
showCupertinoModalBottomSheet(
|
||||
context: context,
|
||||
barrierColor: Colors.black.withAlpha(128),
|
||||
@@ -399,10 +435,10 @@ class _CardsViewState extends State<CardsView> {
|
||||
if (widget.dashboardViewModel.type == WalletType.bitcoin) {
|
||||
final page = getIt.get<NewSendPage>(
|
||||
param1: SendPageParams(
|
||||
initialPaymentRequest: paymentRequest,
|
||||
unspentCoinType: unspentCoinType,
|
||||
mode: SendPageModes.lightningWithdrawal,
|
||||
));
|
||||
initialPaymentRequest: paymentRequest,
|
||||
unspentCoinType: unspentCoinType,
|
||||
mode: SendPageModes.lightningWithdrawal,
|
||||
));
|
||||
showCupertinoModalBottomSheet(
|
||||
context: context,
|
||||
barrierColor: Colors.black.withAlpha(128),
|
||||
@@ -425,4 +461,4 @@ class _CardsViewState extends State<CardsView> {
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,12 @@ import 'package:mobx/mobx.dart';
|
||||
|
||||
class TopBar extends StatefulWidget {
|
||||
const TopBar({
|
||||
required this.lightningMode,
|
||||
required this.onLightningSwitchPress,
|
||||
required this.dashboardViewModel,
|
||||
required this.onSettingsButtonPress,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final bool lightningMode;
|
||||
final VoidCallback onLightningSwitchPress;
|
||||
final VoidCallback onSettingsButtonPress;
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
@@ -73,8 +71,8 @@ class _TopBarState extends State<TopBar> {
|
||||
|
||||
void _bindStatusReaction() {
|
||||
_statusReactionDisposer = reaction(
|
||||
(_) => widget.dashboardViewModel.status.runtimeType,
|
||||
(status) {
|
||||
(_) => widget.dashboardViewModel.status.runtimeType,
|
||||
(status) {
|
||||
syncedMessageTimer?.cancel();
|
||||
syncedMessageTimer = null;
|
||||
|
||||
@@ -107,78 +105,78 @@ class _TopBarState extends State<TopBar> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Padding(
|
||||
padding: EdgeInsets.only(left: 18, right: 18, top: 10 + _additionalTopPadding(context)),
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
widget.dashboardViewModel.hasLightning
|
||||
? LightningSwitcher(
|
||||
lightningMode: widget.lightningMode,
|
||||
onLightningSwitchPress: widget.onLightningSwitchPress,
|
||||
)
|
||||
: ChainIcon(
|
||||
iconPath: widget.dashboardViewModel.wallet.currency.flatIconPath ?? "",
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
isSyncHeavy: widget.dashboardViewModel.isSyncHeavy,
|
||||
showSyncedMessage: showSyncedMessage,
|
||||
padding: EdgeInsets.only(
|
||||
bottom: 10, left: 18, right: 18, top: 10 + _additionalTopPadding(context)),
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
widget.dashboardViewModel.hasLightning
|
||||
? LightningSwitcher(
|
||||
lightningMode: widget.dashboardViewModel.lightningMode,
|
||||
onLightningSwitchPress: widget.onLightningSwitchPress,
|
||||
)
|
||||
: ChainIcon(
|
||||
iconPath: widget.dashboardViewModel.wallet.currency.flatIconPath ?? "",
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
isSyncHeavy: widget.dashboardViewModel.isSyncHeavy,
|
||||
showSyncedMessage: showSyncedMessage,
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 36,
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
reverseDuration: Duration.zero,
|
||||
layoutBuilder: (currentChild, previousChildren) => Stack(
|
||||
alignment: Alignment.centerLeft,
|
||||
children: [
|
||||
...previousChildren,
|
||||
if (currentChild != null) currentChild,
|
||||
],
|
||||
),
|
||||
child: replacesWalletName
|
||||
? SyncBar(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
isSyncHeavy: widget.dashboardViewModel.isSyncHeavy,
|
||||
showSyncedMessage: showSyncedMessage,
|
||||
)
|
||||
: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: WalletInfoBar(
|
||||
name: widget.dashboardViewModel.wallet.name,
|
||||
hardwareWalletType: widget.dashboardViewModel.wallet.hardwareWalletType,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 36,
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
reverseDuration: Duration.zero,
|
||||
layoutBuilder: (currentChild, previousChildren) => Stack(
|
||||
alignment: Alignment.centerLeft,
|
||||
children: [
|
||||
...previousChildren,
|
||||
if (currentChild != null) currentChild,
|
||||
],
|
||||
),
|
||||
child: replacesWalletName
|
||||
? SyncBar(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
isSyncHeavy: widget.dashboardViewModel.isSyncHeavy,
|
||||
showSyncedMessage: showSyncedMessage,
|
||||
)
|
||||
: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: WalletInfoBar(
|
||||
name: widget.dashboardViewModel.wallet.name,
|
||||
hardwareWalletType:
|
||||
widget.dashboardViewModel.wallet.hardwareWalletType,
|
||||
),
|
||||
),
|
||||
if (widget.dashboardViewModel.isTorEnabled) ...[
|
||||
const SizedBox(width: 6),
|
||||
SyncBar(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
isSyncHeavy: widget.dashboardViewModel.isSyncHeavy,
|
||||
showSyncedMessage: showSyncedMessage,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
if (widget.dashboardViewModel.isTorEnabled) ...[
|
||||
const SizedBox(width: 6),
|
||||
SyncBar(
|
||||
dashboardViewModel: widget.dashboardViewModel,
|
||||
isSyncHeavy: widget.dashboardViewModel.isSyncHeavy,
|
||||
showSyncedMessage: showSyncedMessage,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
ModernButton.svg(
|
||||
key: const ValueKey("home_page_settings_button_key"),
|
||||
iconColor: Theme.of(context).colorScheme.primary,
|
||||
size: 36,
|
||||
onPressed: () {
|
||||
HapticFeedback.mediumImpact();
|
||||
widget.onSettingsButtonPress();
|
||||
},
|
||||
svgPath: "assets/new-ui/top-settings.svg",
|
||||
semanticLabel: S.of(context).settings_title,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
ModernButton.svg(
|
||||
key: const ValueKey("home_page_settings_button_key"),
|
||||
iconColor: Theme.of(context).colorScheme.primary,
|
||||
size: 36,
|
||||
onPressed: () {
|
||||
HapticFeedback.mediumImpact();
|
||||
widget.onSettingsButtonPress();
|
||||
},
|
||||
svgPath: "assets/new-ui/top-settings.svg",
|
||||
semanticLabel: S.of(context).settings_title,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
//FIXME remove after this gets fixed flutter-side
|
||||
double _additionalTopPadding(BuildContext context) {
|
||||
@@ -188,4 +186,4 @@ class _TopBarState extends State<TopBar> {
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,7 @@ import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class WalletInfoBar extends StatelessWidget {
|
||||
const WalletInfoBar({
|
||||
required this.name,
|
||||
required this.hardwareWalletType,
|
||||
super.key,
|
||||
});
|
||||
const WalletInfoBar({required this.name, required this.hardwareWalletType, super.key});
|
||||
|
||||
final String name;
|
||||
final HardwareWalletType? hardwareWalletType;
|
||||
@@ -22,6 +18,8 @@ class WalletInfoBar extends StatelessWidget {
|
||||
label: semanticsLabel,
|
||||
child: ExcludeSemantics(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
@@ -33,7 +31,7 @@ class WalletInfoBar extends StatelessWidget {
|
||||
child: hardwareWalletIcon == null
|
||||
? const SizedBox.shrink(key: ValueKey("empty"))
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(right: 4),
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: CakeImageWidget(
|
||||
imageUrl: hardwareWalletIcon!,
|
||||
key: const ValueKey("hardware_wallet_icon"),
|
||||
|
||||
Reference in New Issue
Block a user