mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
CW-580: BIP39 Wallets Shared Seed Implementation: "One Seed - Multiple Wallets" (#1307)
* feat: Implement creating new BIP39 wallet with same seed used for other owned BIP39 wallets * feat: Use same seed for BIP39 Wallets * Update pre_existing_seeds_page.dart * Feat: BIP39 Same seed wallet creation using the Common Parent Wallet Strategy * feat: Finalize implementing preexisting seeds * feat: Implement shared bip39 wallet seed for Bitcoin wallet type * feat: Implement shared bip39 wallet seed for Litecoin wallet type * feat: Implement shared bip39 wallet seed for BitcoinCash wallet type * feat: Implement shared bip39 wallet seed for Nano wallet type, although disabled entry for now * fix: Remove non bip39 seed wallet type from listing * feat: Implement grouped and single wallets lists in wallets listing page and implement editing and saving group names * fix: Issue where the ontap always references the leadwallet, also make shared seed wallets section header only display when the multi wallet groups list is not empty * fix: Add translation and adjust the way the groups display * feat: Activate bip39 as an option for creating Nano wallet types * fix: Handle edgecase with creating new wallet with group address, handle case where only bip39 derivation type is allowed with child wallets, activate nano wallet type for shared seed * chore: Modify the UI to fit adjustment made on figma * fix: Disposed box triggering error in hive and causing wallet list view to display error * fix: Switch wallet groups title in wallets list page and also fix issue with renaming groups * Update lib/reactions/bip39_wallet_utils.dart [skip ci] Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Update lib/router.dart [skip ci] Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix: Review fixes: Combine New Wallet Page Type arguments into a single model * fix: Review fixes: Add failure guard when fetching mnemonic for selected wallet in pre-existing wallets page * fix: Review fixes - Add loading indicator when mnemonic is being selected for wallet * fix: Review fixes - Modify variable name to avoid clashes * fix: Review fixes - Access WalletManager through dependency injection instead of service location * fix: Review fixes - Add testnet to convertWalletInfoToWalletlistItem function, and adjust according where used * fix: Review fixes - Add walletPassword to nano, tron and wownero wallets and confirm it is properly handled as it should be * fix: Remove leadWallet, modify filtering flow to reflect this and not depend on leadWallet, and adjust privacy settings * fix: Review Fixes - Modify restore flow to reflect current nature of bip39 as default for majority of wallet types * fix: QA Fixes - Modify preexisting page to display wallet group names if set, and display them in incremental order if not set * fix: Add wallet group description page and rename pre-existingseeds page to wallet group display page * fix: Product Fix - Rename pre-existing seeds file name to wallet group display filename * fix: Product fix - Separate multiwallets groups from single wallets and display separately * fix - Product Fix - Add empty state for wallet group listing when creating a new wallet, adjust CTAs across buttons relating to the flow also --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
Omar Hatem
parent
3a391f10a3
commit
4e2e5e708c
@@ -28,10 +28,22 @@ class CWBitcoin extends Bitcoin {
|
||||
name: name, password: password, wif: wif, walletInfo: walletInfo);
|
||||
|
||||
@override
|
||||
WalletCredentials createBitcoinNewWalletCredentials(
|
||||
{required String name, WalletInfo? walletInfo, String? password, String? passphrase}) =>
|
||||
WalletCredentials createBitcoinNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? passphrase,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
}) =>
|
||||
BitcoinNewWalletCredentials(
|
||||
name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createBitcoinHardwareWalletCredentials(
|
||||
|
||||
@@ -16,9 +16,17 @@ class CWBitcoinCash extends BitcoinCash {
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? passphrase,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
}) =>
|
||||
BitcoinCashNewWalletCredentials(
|
||||
name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
parentAddress: parentAddress,
|
||||
mnemonic: mnemonic,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createBitcoinCashRestoreWalletFromSeedCredentials(
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
class NewWalletArguments {
|
||||
final WalletType type;
|
||||
final String? mnemonic;
|
||||
final String? parentAddress;
|
||||
final bool isChildWallet;
|
||||
|
||||
NewWalletArguments({
|
||||
required this.type,
|
||||
this.parentAddress,
|
||||
this.mnemonic,
|
||||
this.isChildWallet = false,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NewWalletTypeArguments {
|
||||
final void Function(BuildContext, WalletType)? onTypeSelected;
|
||||
final bool isCreate;
|
||||
final bool isHardwareWallet;
|
||||
|
||||
NewWalletTypeArguments({
|
||||
required this.onTypeSelected,
|
||||
required this.isCreate,
|
||||
required this.isHardwareWallet,
|
||||
});
|
||||
}
|
||||
+53
-21
@@ -12,10 +12,12 @@ import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/core/backup_service.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
||||
import 'package:cake_wallet/core/secure_storage.dart';
|
||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
|
||||
@@ -30,6 +32,8 @@ import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
||||
import 'package:cake_wallet/entities/wallet_manager.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/address_list_page.dart';
|
||||
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
@@ -145,7 +149,9 @@ import 'package:cake_wallet/view_model/cake_pay/cake_pay_cards_list_view_model.d
|
||||
import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/new_wallet_type_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_groups_display_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
|
||||
@@ -157,7 +163,6 @@ import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart
|
||||
import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
|
||||
import 'package:cw_core/nano_account.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
@@ -358,13 +363,31 @@ Future<void> setup({
|
||||
getIt.get<KeyService>(),
|
||||
(WalletType type) => getIt.get<WalletService>(param1: type)));
|
||||
|
||||
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) => WalletNewVM(
|
||||
getIt.registerFactoryParam<WalletNewVM, NewWalletArguments, void>(
|
||||
(newWalletArgs, _) => WalletNewVM(
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletCreationService>(param1: type),
|
||||
getIt.get<WalletCreationService>(param1:newWalletArgs.type),
|
||||
_walletInfoSource,
|
||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
|
||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: newWalletArgs.type),
|
||||
getIt.get<SeedSettingsViewModel>(),
|
||||
type: type));
|
||||
newWalletArguments: newWalletArgs,));
|
||||
|
||||
|
||||
getIt.registerFactory<NewWalletTypeViewModel>(() => NewWalletTypeViewModel(_walletInfoSource));
|
||||
|
||||
getIt.registerFactory<WalletManager>(
|
||||
() => WalletManager(_walletInfoSource, getIt.get<SharedPreferences>()),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<WalletGroupsDisplayViewModel, WalletType, void>(
|
||||
(type, _) => WalletGroupsDisplayViewModel(
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletLoadingService>(),
|
||||
getIt.get<WalletManager>(),
|
||||
getIt.get<WalletListViewModel>(),
|
||||
type: type,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) {
|
||||
return WalletUnlockPage(
|
||||
@@ -723,6 +746,7 @@ Future<void> setup({
|
||||
_walletInfoSource,
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletLoadingService>(),
|
||||
getIt.get<WalletManager>(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
@@ -733,6 +757,7 @@ Future<void> setup({
|
||||
_walletInfoSource,
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletLoadingService>(),
|
||||
getIt.get<WalletManager>(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -743,17 +768,28 @@ Future<void> setup({
|
||||
));
|
||||
|
||||
getIt.registerFactoryParam<WalletEditViewModel, WalletListViewModel, void>(
|
||||
(WalletListViewModel walletListViewModel, _) =>
|
||||
WalletEditViewModel(walletListViewModel, getIt.get<WalletLoadingService>()));
|
||||
(WalletListViewModel walletListViewModel, _) => WalletEditViewModel(
|
||||
walletListViewModel,
|
||||
getIt.get<WalletLoadingService>(),
|
||||
getIt.get<WalletManager>(),
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<WalletEditPage, WalletEditPageArguments, void>((arguments, _) {
|
||||
|
||||
getIt.registerFactoryParam<WalletEditPage, List<dynamic>, void>((args, _) {
|
||||
final walletListViewModel = args.first as WalletListViewModel;
|
||||
final editingWallet = args.last as WalletListItem;
|
||||
return WalletEditPage(
|
||||
walletEditViewModel: getIt.get<WalletEditViewModel>(param1: walletListViewModel),
|
||||
pageArguments: WalletEditPageArguments(
|
||||
walletEditViewModel: getIt.get<WalletEditViewModel>(param1: arguments.walletListViewModel),
|
||||
authService: getIt.get<AuthService>(),
|
||||
walletNewVM: getIt.get<WalletNewVM>(param1: editingWallet.type),
|
||||
editingWallet: editingWallet);
|
||||
walletNewVM: getIt.get<WalletNewVM>(
|
||||
param1: NewWalletArguments(type: arguments.editingWallet.type),
|
||||
),
|
||||
editingWallet: arguments.editingWallet,
|
||||
isWalletGroup: arguments.isWalletGroup,
|
||||
groupName: arguments.groupName,
|
||||
parentAddress: arguments.parentAddress,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
getIt.registerFactory<NanoAccountListViewModel>(() {
|
||||
@@ -1060,15 +1096,11 @@ Future<void> setup({
|
||||
transactionDetailsViewModel:
|
||||
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
|
||||
|
||||
getIt.registerFactoryParam<NewWalletTypePage, void Function(BuildContext, WalletType),
|
||||
List<bool>?>((param1, additionalParams) {
|
||||
final isCreate = additionalParams?[0] ?? true;
|
||||
final isHardwareWallet = additionalParams?[1] ?? false;
|
||||
|
||||
getIt.registerFactoryParam<NewWalletTypePage, NewWalletTypeArguments, void>(
|
||||
(newWalletTypeArguments, _) {
|
||||
return NewWalletTypePage(
|
||||
onTypeSelected: param1,
|
||||
isCreate: isCreate,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
newWalletTypeArguments: newWalletTypeArguments,
|
||||
newWalletTypeViewModel: getIt.get<NewWalletTypeViewModel>(),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ class PreferencesKey {
|
||||
static const autoGenerateSubaddressStatusKey = 'auto_generate_subaddress_status';
|
||||
static const moneroSeedType = 'monero_seed_type';
|
||||
static const bitcoinSeedType = 'bitcoin_seed_type';
|
||||
static const nanoSeedType = 'nano_seed_type';
|
||||
static const clearnetDonationLink = 'clearnet_donation_link';
|
||||
static const onionDonationLink = 'onion_donation_link';
|
||||
static const donationLinkWalletName = 'donation_link_wallet_name';
|
||||
|
||||
@@ -65,3 +65,28 @@ class BitcoinSeedType extends EnumerableItem<int> with Serializable<int> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NanoSeedType extends EnumerableItem<int> with Serializable<int> {
|
||||
const NanoSeedType(this.type, {required String title, required int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
final DerivationType type;
|
||||
|
||||
static const all = [NanoSeedType.nanoStandard, NanoSeedType.bip39];
|
||||
|
||||
static const defaultDerivationType = bip39;
|
||||
|
||||
static const nanoStandard = NanoSeedType(DerivationType.nano, raw: 0, title: 'Nano');
|
||||
static const bip39 = NanoSeedType(DerivationType.bip39, raw: 1, title: 'BIP39');
|
||||
|
||||
static NanoSeedType deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return nanoStandard;
|
||||
case 1:
|
||||
return bip39;
|
||||
default:
|
||||
throw Exception('Unexpected token: $raw for SeedType deserialize');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||
|
||||
class WalletEditPageArguments {
|
||||
WalletEditPageArguments({
|
||||
required this.editingWallet,
|
||||
this.isWalletGroup = false,
|
||||
this.walletListViewModel,
|
||||
this.groupName = '',
|
||||
this.parentAddress = '',
|
||||
this.walletEditViewModel,
|
||||
this.walletNewVM,
|
||||
this.authService,
|
||||
});
|
||||
|
||||
final WalletListItem editingWallet;
|
||||
final bool isWalletGroup;
|
||||
final String groupName;
|
||||
final String parentAddress;
|
||||
final WalletListViewModel? walletListViewModel;
|
||||
|
||||
final WalletEditViewModel? walletEditViewModel;
|
||||
final WalletNewVM? walletNewVM;
|
||||
final AuthService? authService;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class WalletGroup {
|
||||
WalletGroup(this.parentAddress) : wallets = [];
|
||||
|
||||
/// Main identifier for each group, compulsory.
|
||||
final String parentAddress;
|
||||
|
||||
/// Child wallets that share the same parent address within this group
|
||||
List<WalletInfo> wallets;
|
||||
|
||||
/// Custom name for the group, editable for multi-child wallet groups
|
||||
String? groupName;
|
||||
|
||||
/// Allows editing of the group name (only for multi-child groups).
|
||||
void setCustomName(String name) {
|
||||
if (wallets.length > 1) {
|
||||
groupName = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import 'package:cake_wallet/entities/wallet_group.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class WalletManager {
|
||||
WalletManager(
|
||||
this._walletInfoSource,
|
||||
this._sharedPreferences,
|
||||
);
|
||||
|
||||
final Box<WalletInfo> _walletInfoSource;
|
||||
final SharedPreferences _sharedPreferences;
|
||||
|
||||
final List<WalletGroup> walletGroups = [];
|
||||
|
||||
/// Categorize wallets into groups based on their parentAddress.
|
||||
///
|
||||
/// Update the lead wallet for each group and clean up empty groups
|
||||
/// i.e remove group if there's no lead wallet (i.e, no wallets left)
|
||||
void updateWalletGroups() {
|
||||
walletGroups.clear();
|
||||
|
||||
for (var walletInfo in _walletInfoSource.values) {
|
||||
final group = _getOrCreateGroup(_resolveParentAddress(walletInfo));
|
||||
group.wallets.add(walletInfo);
|
||||
}
|
||||
|
||||
walletGroups.removeWhere((group) => group.wallets.isEmpty);
|
||||
|
||||
_loadCustomGroupNames();
|
||||
}
|
||||
|
||||
/// Function to determine the correct parentAddress for a wallet.
|
||||
///
|
||||
/// If it's a parent wallet (parentAddress is null),
|
||||
/// use its own address as parentAddress.
|
||||
String _resolveParentAddress(WalletInfo walletInfo) {
|
||||
return walletInfo.parentAddress ?? walletInfo.address;
|
||||
}
|
||||
|
||||
/// Check if a group with the parentAddress already exists,
|
||||
/// If no group exists, create a new one.
|
||||
///
|
||||
WalletGroup _getOrCreateGroup(String parentAddress) {
|
||||
return walletGroups.firstWhere(
|
||||
(group) => group.parentAddress == parentAddress,
|
||||
orElse: () {
|
||||
final newGroup = WalletGroup(parentAddress);
|
||||
walletGroups.add(newGroup);
|
||||
return newGroup;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Add a new wallet and update lead wallet after adding.
|
||||
void addWallet(WalletInfo walletInfo) {
|
||||
final group = _getOrCreateGroup(_resolveParentAddress(walletInfo));
|
||||
group.wallets.add(walletInfo);
|
||||
}
|
||||
|
||||
/// Removes a wallet from a group i.e when it's deleted.
|
||||
///
|
||||
/// Update lead wallet after removing,
|
||||
/// Remove the group if it's empty (i.e., no lead wallet).
|
||||
void removeWallet(WalletInfo walletInfo) {
|
||||
final group = _getOrCreateGroup(_resolveParentAddress(walletInfo));
|
||||
group.wallets.remove(walletInfo);
|
||||
|
||||
if (group.wallets.isEmpty) {
|
||||
walletGroups.remove(group);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all the child wallets within a group.
|
||||
///
|
||||
/// If the group is not found, returns an empty group with no wallets.
|
||||
List<WalletInfo> getWalletsInGroup(String parentAddress) {
|
||||
return walletGroups
|
||||
.firstWhere(
|
||||
(group) => group.parentAddress == parentAddress,
|
||||
orElse: () => WalletGroup(parentAddress),
|
||||
)
|
||||
.wallets;
|
||||
}
|
||||
|
||||
/// Iterate through all groups and load their custom names from storage
|
||||
void _loadCustomGroupNames() {
|
||||
for (var group in walletGroups) {
|
||||
final groupName = _sharedPreferences.getString('wallet_group_name_${group.parentAddress}');
|
||||
if (groupName != null && group.wallets.length > 1) {
|
||||
group.groupName = groupName; // Restore custom name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Save custom name for a group
|
||||
void _saveCustomGroupName(String parentAddress, String name) {
|
||||
_sharedPreferences.setString('wallet_group_name_$parentAddress', name);
|
||||
}
|
||||
|
||||
// Set custom group name and persist it
|
||||
void setGroupName(String parentAddress, String name) {
|
||||
if (parentAddress.isEmpty || name.isEmpty) return;
|
||||
|
||||
final group = walletGroups.firstWhere((group) => group.parentAddress == parentAddress);
|
||||
group.setCustomName(name);
|
||||
_saveCustomGroupName(parentAddress, name); // Persist the custom name
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,18 @@ class CWEthereum extends Ethereum {
|
||||
@override
|
||||
WalletCredentials createEthereumNewWalletCredentials({
|
||||
required String name,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
}) =>
|
||||
EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
|
||||
EVMChainNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
parentAddress: parentAddress,
|
||||
mnemonic: mnemonic,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createEthereumRestoreWalletFromSeedCredentials({
|
||||
|
||||
@@ -91,12 +91,17 @@ class CWNano extends Nano {
|
||||
@override
|
||||
WalletCredentials createNanoNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
}) =>
|
||||
NanoNewWalletCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
derivationType: DerivationType.nano,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: parentAddress,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
|
||||
@override
|
||||
|
||||
+28
-21
@@ -8,12 +8,19 @@ class CWPolygon extends Polygon {
|
||||
PolygonWalletService(walletInfoSource, isDirect, client: PolygonClient());
|
||||
|
||||
@override
|
||||
WalletCredentials createPolygonNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password
|
||||
}) =>
|
||||
EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
|
||||
WalletCredentials createPolygonNewWalletCredentials(
|
||||
{required String name,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
WalletInfo? walletInfo,
|
||||
String? password}) =>
|
||||
EVMChainNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createPolygonRestoreWalletFromSeedCredentials({
|
||||
@@ -77,21 +84,21 @@ class CWPolygon extends Polygon {
|
||||
int? feeRate,
|
||||
}) =>
|
||||
EVMChainTransactionCredentials(
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
.toList(),
|
||||
priority: priority as EVMChainTransactionPriority,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
.toList(),
|
||||
priority: priority as EVMChainTransactionPriority,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
|
||||
Object createPolygonTransactionCredentialsRaw(
|
||||
List<OutputInfo> outputs, {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
bool isBIP39Wallet(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return true;
|
||||
case WalletType.monero:
|
||||
case WalletType.wownero:
|
||||
case WalletType.haven:
|
||||
case WalletType.none:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+99
-31
@@ -1,11 +1,14 @@
|
||||
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
||||
import 'package:cake_wallet/entities/wallet_nft_response.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
@@ -40,9 +43,11 @@ import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/wallet_group_display_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/advanced_privacy_settings_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/wallet_group_description_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
|
||||
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
|
||||
@@ -104,6 +109,7 @@ import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_groups_display_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||
@@ -134,7 +140,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
if (SettingsStoreBase.walletPasswordDirectInput) {
|
||||
if (availableWalletTypes.length == 1) {
|
||||
return createRoute(
|
||||
RouteSettings(name: Routes.newWallet, arguments: availableWalletTypes.first));
|
||||
RouteSettings(
|
||||
name: Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: availableWalletTypes.first),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return createRoute(RouteSettings(name: Routes.newWalletType));
|
||||
}
|
||||
@@ -144,8 +154,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
builder: (_) =>
|
||||
getIt.get<SetupPinCodePage>(param1: (PinCodeState<PinCodeWidget> context, dynamic _) {
|
||||
if (availableWalletTypes.length == 1) {
|
||||
Navigator.of(context.context)
|
||||
.pushNamed(Routes.newWallet, arguments: availableWalletTypes.first);
|
||||
Navigator.of(context.context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: availableWalletTypes.first),
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context.context).pushNamed(Routes.newWalletType);
|
||||
}
|
||||
@@ -154,17 +166,38 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
|
||||
case Routes.newWalletType:
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(Routes.newWallet, arguments: type)));
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: type),
|
||||
),
|
||||
isCreate: true,
|
||||
isHardwareWallet: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
case Routes.walletGroupsDisplayPage:
|
||||
final type = settings.arguments as WalletType;
|
||||
final walletGroupsDisplayVM = getIt.get<WalletGroupsDisplayViewModel>(param1: type);
|
||||
|
||||
return CupertinoPageRoute<void>(builder: (_) => WalletGroupsDisplayPage(walletGroupsDisplayVM));
|
||||
|
||||
case Routes.newWallet:
|
||||
final type = settings.arguments as WalletType;
|
||||
final walletNewVM = getIt.get<WalletNewVM>(param1: type);
|
||||
final args = settings.arguments as NewWalletArguments;
|
||||
|
||||
final walletNewVM = getIt.get<WalletNewVM>(param1: args);
|
||||
final seedSettingsViewModel = getIt.get<SeedSettingsViewModel>();
|
||||
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => NewWalletPage(walletNewVM, seedSettingsViewModel));
|
||||
builder: (_) => NewWalletPage(
|
||||
walletNewVM,
|
||||
seedSettingsViewModel,
|
||||
isChildWallet: args.isChildWallet,
|
||||
),
|
||||
);
|
||||
|
||||
case Routes.chooseHardwareWalletAccount:
|
||||
final arguments = settings.arguments as List<dynamic>;
|
||||
@@ -185,10 +218,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
|
||||
case Routes.restoreWalletType:
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||
param2: [false, false]));
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||
isCreate: false,
|
||||
isHardwareWallet: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
case Routes.restoreOptions:
|
||||
if (SettingsStoreBase.walletPasswordDirectInput) {
|
||||
@@ -220,10 +258,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
builder: (_) => getIt.get<WalletRestorePage>(param1: availableWalletTypes.first));
|
||||
} else {
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||
param2: [false, false]));
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||
isCreate: false,
|
||||
isHardwareWallet: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
case Routes.restoreWalletFromHardwareWallet:
|
||||
@@ -252,23 +295,35 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
));
|
||||
} else {
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: (BuildContext context, WalletType type) {
|
||||
final arguments = ConnectDevicePageParams(
|
||||
walletType: type,
|
||||
onConnectDevice: (BuildContext context, _) => Navigator.of(context)
|
||||
.pushNamed(Routes.chooseHardwareWalletAccount, arguments: [type]),
|
||||
);
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) {
|
||||
final arguments = ConnectDevicePageParams(
|
||||
walletType: type,
|
||||
onConnectDevice: (BuildContext context, _) => Navigator.of(context)
|
||||
.pushNamed(Routes.chooseHardwareWalletAccount, arguments: [type]),
|
||||
);
|
||||
|
||||
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
|
||||
},
|
||||
param2: [false, true]));
|
||||
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
|
||||
},
|
||||
isCreate: false,
|
||||
isHardwareWallet: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
case Routes.restoreWalletTypeFromQR:
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: (BuildContext context, WalletType type) => Navigator.of(context).pop(type)));
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pop(type),
|
||||
isCreate: false,
|
||||
isHardwareWallet: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
case Routes.seed:
|
||||
return MaterialPageRoute<void>(
|
||||
@@ -341,8 +396,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
|
||||
case Routes.walletEdit:
|
||||
return MaterialPageRoute<void>(
|
||||
fullscreenDialog: true,
|
||||
builder: (_) => getIt.get<WalletEditPage>(param1: settings.arguments as List<dynamic>));
|
||||
fullscreenDialog: true,
|
||||
builder: (_) =>
|
||||
getIt.get<WalletEditPage>(param1: settings.arguments as WalletEditPageArguments),
|
||||
);
|
||||
|
||||
case Routes.auth:
|
||||
return MaterialPageRoute<void>(
|
||||
@@ -592,12 +649,14 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
final args = settings.arguments as Map<String, dynamic>;
|
||||
final type = args['type'] as WalletType;
|
||||
final isFromRestore = args['isFromRestore'] as bool? ?? false;
|
||||
final isChildWallet = args['isChildWallet'] as bool? ?? false;
|
||||
final useTestnet = args['useTestnet'] as bool;
|
||||
final toggleTestnet = args['toggleTestnet'] as Function(bool? val);
|
||||
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => AdvancedPrivacySettingsPage(
|
||||
isFromRestore: isFromRestore,
|
||||
isChildWallet: isChildWallet,
|
||||
useTestnet: useTestnet,
|
||||
toggleUseTestnet: toggleTestnet,
|
||||
advancedPrivacySettingsViewModel:
|
||||
@@ -712,6 +771,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => ConnectDevicePage(params, getIt.get<LedgerViewModel>()));
|
||||
|
||||
case Routes.walletGroupDescription:
|
||||
final walletType = settings.arguments as WalletType;
|
||||
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => WalletGroupDescriptionPage(
|
||||
selectedWalletType: walletType,
|
||||
),
|
||||
);
|
||||
|
||||
default:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => Scaffold(
|
||||
|
||||
+5
-4
@@ -8,8 +8,7 @@ class Routes {
|
||||
static const restoreWalletFromSeedKeys = '/restore_wallet_from_seeds_keys';
|
||||
static const restoreWalletFromHardwareWallet = '/restore/hardware_wallet';
|
||||
static const restoreWalletTypeFromQR = '/restore_wallet_from_qr_code';
|
||||
static const restoreWalletChooseDerivation =
|
||||
'/restore_wallet_choose_derivation';
|
||||
static const restoreWalletChooseDerivation = '/restore_wallet_choose_derivation';
|
||||
static const chooseHardwareWalletAccount = '/restore/hardware_wallet/accounts';
|
||||
static const dashboard = '/dashboard';
|
||||
static const send = '/send';
|
||||
@@ -99,11 +98,13 @@ class Routes {
|
||||
static const editToken = '/edit_token';
|
||||
static const manageNodes = '/manage_nodes';
|
||||
static const managePowNodes = '/manage_pow_nodes';
|
||||
static const walletConnectConnectionsListing =
|
||||
'/wallet-connect-connections-listing';
|
||||
static const walletConnectConnectionsListing = '/wallet-connect-connections-listing';
|
||||
static const nftDetailsPage = '/nft_details_page';
|
||||
static const importNFTPage = '/import_nft_page';
|
||||
static const torPage = '/tor_page';
|
||||
|
||||
static const signPage = '/sign_page';
|
||||
static const connectDevices = '/device/connect';
|
||||
static const walletGroupsDisplayPage = '/wallet_groups_display_page';
|
||||
static const walletGroupDescription = '/wallet_group_description';
|
||||
}
|
||||
|
||||
@@ -10,10 +10,18 @@ class CWSolana extends Solana {
|
||||
@override
|
||||
WalletCredentials createSolanaNewWalletCredentials({
|
||||
required String name,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
}) =>
|
||||
SolanaNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
|
||||
SolanaNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createSolanaRestoreWalletFromSeedCredentials({
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:another_flushbar/flushbar.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/entities/desktop_dropdown_item.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
@@ -219,7 +221,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||
widget._authService.authenticateAction(
|
||||
context,
|
||||
route: Routes.newWallet,
|
||||
arguments: widget.walletListViewModel.currentWalletType,
|
||||
arguments: NewWalletArguments(type: widget.walletListViewModel.currentWalletType),
|
||||
conditionToDetermineIfToUse2FA:
|
||||
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
);
|
||||
|
||||
@@ -9,10 +9,12 @@ import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
|
||||
@@ -24,6 +26,7 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
class AdvancedPrivacySettingsPage extends BasePage {
|
||||
AdvancedPrivacySettingsPage({
|
||||
required this.isFromRestore,
|
||||
required this.isChildWallet,
|
||||
required this.useTestnet,
|
||||
required this.toggleUseTestnet,
|
||||
required this.advancedPrivacySettingsViewModel,
|
||||
@@ -39,25 +42,40 @@ class AdvancedPrivacySettingsPage extends BasePage {
|
||||
String get title => S.current.privacy_settings;
|
||||
|
||||
final bool isFromRestore;
|
||||
final bool isChildWallet;
|
||||
final bool useTestnet;
|
||||
final Function(bool? val) toggleUseTestnet;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => _AdvancedPrivacySettingsBody(isFromRestore, useTestnet,
|
||||
toggleUseTestnet, advancedPrivacySettingsViewModel, nodeViewModel, seedSettingsViewModel);
|
||||
Widget body(BuildContext context) => _AdvancedPrivacySettingsBody(
|
||||
isFromRestore,
|
||||
isChildWallet,
|
||||
useTestnet,
|
||||
toggleUseTestnet,
|
||||
advancedPrivacySettingsViewModel,
|
||||
nodeViewModel,
|
||||
seedSettingsViewModel,
|
||||
);
|
||||
}
|
||||
|
||||
class _AdvancedPrivacySettingsBody extends StatefulWidget {
|
||||
const _AdvancedPrivacySettingsBody(this.isFromRestore, this.useTestnet, this.toggleUseTestnet,
|
||||
this.privacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel,
|
||||
{Key? key})
|
||||
: super(key: key);
|
||||
const _AdvancedPrivacySettingsBody(
|
||||
this.isFromRestore,
|
||||
this.isChildWallet,
|
||||
this.useTestnet,
|
||||
this.toggleUseTestnet,
|
||||
this.privacySettingsViewModel,
|
||||
this.nodeViewModel,
|
||||
this.seedTypeViewModel, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final AdvancedPrivacySettingsViewModel privacySettingsViewModel;
|
||||
final NodeCreateOrEditViewModel nodeViewModel;
|
||||
final SeedSettingsViewModel seedTypeViewModel;
|
||||
|
||||
final bool isFromRestore;
|
||||
final bool isChildWallet;
|
||||
final bool useTestnet;
|
||||
final Function(bool? val) toggleUseTestnet;
|
||||
|
||||
@@ -78,6 +96,16 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
|
||||
passphraseController
|
||||
.addListener(() => widget.seedTypeViewModel.setPassphrase(passphraseController.text));
|
||||
|
||||
if (widget.isChildWallet) {
|
||||
if (widget.privacySettingsViewModel.type == WalletType.bitcoin) {
|
||||
widget.seedTypeViewModel.setBitcoinSeedType(BitcoinSeedType.bip39);
|
||||
}
|
||||
|
||||
if (widget.privacySettingsViewModel.type == WalletType.nano) {
|
||||
widget.seedTypeViewModel.setNanoSeedType(NanoSeedType.bip39);
|
||||
}
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@@ -116,7 +144,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (widget.privacySettingsViewModel.hasSeedTypeOption)
|
||||
if (widget.privacySettingsViewModel.isMoneroSeedTypeOptionsEnabled)
|
||||
Observer(builder: (_) {
|
||||
return SettingsChoicesCell(
|
||||
ChoicesListItem<MoneroSeedType>(
|
||||
@@ -127,15 +155,37 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
),
|
||||
);
|
||||
}),
|
||||
if ([WalletType.bitcoin, WalletType.litecoin]
|
||||
.contains(widget.privacySettingsViewModel.type))
|
||||
if (widget.privacySettingsViewModel.isBitcoinSeedTypeOptionsEnabled)
|
||||
Observer(builder: (_) {
|
||||
return SettingsChoicesCell(
|
||||
ChoicesListItem<BitcoinSeedType>(
|
||||
title: S.current.seedtype,
|
||||
items: BitcoinSeedType.all,
|
||||
selectedItem: widget.seedTypeViewModel.bitcoinSeedType,
|
||||
onItemSelected: widget.seedTypeViewModel.setBitcoinSeedType,
|
||||
onItemSelected: (type) {
|
||||
if (widget.isChildWallet && type != BitcoinSeedType.bip39) {
|
||||
showAlertForSelectingNonBIP39DerivationTypeForChildWallets();
|
||||
} else {
|
||||
widget.seedTypeViewModel.setBitcoinSeedType(type);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (widget.privacySettingsViewModel.isNanoSeedTypeOptionsEnabled)
|
||||
Observer(builder: (_) {
|
||||
return SettingsChoicesCell(
|
||||
ChoicesListItem<NanoSeedType>(
|
||||
title: S.current.seedtype,
|
||||
items: NanoSeedType.all,
|
||||
selectedItem: widget.seedTypeViewModel.nanoSeedType,
|
||||
onItemSelected: (type) {
|
||||
if (widget.isChildWallet && type != NanoSeedType.bip39) {
|
||||
showAlertForSelectingNonBIP39DerivationTypeForChildWallets();
|
||||
} else {
|
||||
widget.seedTypeViewModel.setNanoSeedType(type);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -256,6 +306,19 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
);
|
||||
}
|
||||
|
||||
void showAlertForSelectingNonBIP39DerivationTypeForChildWallets() {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.current.seedtype_alert_title,
|
||||
alertContent: S.current.seedtype_alert_content,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
passphraseController
|
||||
|
||||
@@ -26,10 +26,15 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class NewWalletPage extends BasePage {
|
||||
NewWalletPage(this._walletNewVM, this._seedSettingsViewModel);
|
||||
NewWalletPage(
|
||||
this._walletNewVM,
|
||||
this._seedSettingsViewModel, {
|
||||
this.isChildWallet = false,
|
||||
});
|
||||
|
||||
final WalletNewVM _walletNewVM;
|
||||
final SeedSettingsViewModel _seedSettingsViewModel;
|
||||
final bool isChildWallet;
|
||||
|
||||
final walletNameImage = Image.asset('assets/images/wallet_name.png');
|
||||
|
||||
@@ -48,15 +53,23 @@ class NewWalletPage extends BasePage {
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletNameForm(
|
||||
_walletNewVM,
|
||||
currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage,
|
||||
_seedSettingsViewModel);
|
||||
_walletNewVM,
|
||||
currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage,
|
||||
_seedSettingsViewModel,
|
||||
isChildWallet,
|
||||
);
|
||||
}
|
||||
|
||||
class WalletNameForm extends StatefulWidget {
|
||||
WalletNameForm(this._walletNewVM, this.walletImage, this._seedSettingsViewModel);
|
||||
WalletNameForm(
|
||||
this._walletNewVM,
|
||||
this.walletImage,
|
||||
this._seedSettingsViewModel,
|
||||
this.isChildWallet,
|
||||
);
|
||||
|
||||
final WalletNewVM _walletNewVM;
|
||||
final bool isChildWallet;
|
||||
final Image walletImage;
|
||||
final SeedSettingsViewModel _seedSettingsViewModel;
|
||||
|
||||
@@ -338,7 +351,8 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||
Navigator.of(context).pushNamed(Routes.advancedPrivacySettings, arguments: {
|
||||
"type": _walletNewVM.type,
|
||||
"useTestnet": _walletNewVM.useTestnet,
|
||||
"toggleTestnet": _walletNewVM.toggleUseTestnet
|
||||
"toggleTestnet": _walletNewVM.toggleUseTestnet,
|
||||
"isChildWallet": widget.isChildWallet,
|
||||
});
|
||||
},
|
||||
child: Text(S.of(context).advanced_settings),
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/reactions/bip39_wallet_utils.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/widgets/popup_cancellable_alert.dart';
|
||||
@@ -11,6 +15,7 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/new_wallet_type_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_types.g.dart';
|
||||
import 'package:cw_core/hardware/device_connection_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
@@ -18,21 +23,20 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class NewWalletTypePage extends BasePage {
|
||||
NewWalletTypePage({
|
||||
required this.onTypeSelected,
|
||||
required this.isCreate,
|
||||
required this.isHardwareWallet,
|
||||
required this.newWalletTypeViewModel,
|
||||
required this.newWalletTypeArguments,
|
||||
});
|
||||
|
||||
final void Function(BuildContext, WalletType) onTypeSelected;
|
||||
final bool isCreate;
|
||||
final bool isHardwareWallet;
|
||||
final NewWalletTypeViewModel newWalletTypeViewModel;
|
||||
final NewWalletTypeArguments newWalletTypeArguments;
|
||||
|
||||
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
|
||||
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
|
||||
|
||||
@override
|
||||
String get title =>
|
||||
isCreate ? S.current.wallet_list_create_new_wallet : S.current.wallet_list_restore_wallet;
|
||||
String get title => newWalletTypeArguments.isCreate
|
||||
? S.current.wallet_list_create_new_wallet
|
||||
: S.current.wallet_list_restore_wallet;
|
||||
|
||||
@override
|
||||
Function(BuildContext)? get pushToNextWidget => (context) {
|
||||
@@ -44,24 +48,27 @@ class NewWalletTypePage extends BasePage {
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletTypeForm(
|
||||
onTypeSelected: onTypeSelected,
|
||||
walletImage: currentTheme.type == ThemeType.dark ? walletTypeImage : walletTypeLightImage,
|
||||
isCreate: isCreate,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
isCreate: newWalletTypeArguments.isCreate,
|
||||
newWalletTypeViewModel: newWalletTypeViewModel,
|
||||
onTypeSelected: newWalletTypeArguments.onTypeSelected,
|
||||
isHardwareWallet: newWalletTypeArguments.isHardwareWallet,
|
||||
);
|
||||
}
|
||||
|
||||
class WalletTypeForm extends StatefulWidget {
|
||||
WalletTypeForm({
|
||||
required this.onTypeSelected,
|
||||
required this.walletImage,
|
||||
required this.isCreate,
|
||||
required this.newWalletTypeViewModel,
|
||||
this.onTypeSelected,
|
||||
required this.isHardwareWallet,
|
||||
});
|
||||
|
||||
final void Function(BuildContext, WalletType) onTypeSelected;
|
||||
final Image walletImage;
|
||||
final bool isCreate;
|
||||
final Image walletImage;
|
||||
final NewWalletTypeViewModel newWalletTypeViewModel;
|
||||
final void Function(BuildContext, WalletType)? onTypeSelected;
|
||||
final bool isHardwareWallet;
|
||||
|
||||
@override
|
||||
@@ -179,6 +186,18 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
);
|
||||
}
|
||||
|
||||
widget.onTypeSelected(context, selected!);
|
||||
// If it's a restore flow, trigger the external callback
|
||||
// If it's not a BIP39 Wallet or if there are no other wallets, route to the newWallet page
|
||||
// Any other scenario, route to pre-existing seed page
|
||||
if (!widget.isCreate) {
|
||||
widget.onTypeSelected!(context, selected!);
|
||||
} else if (!isBIP39Wallet(selected!) || !widget.newWalletTypeViewModel.hasExisitingWallet) {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: selected!),
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).pushNamed(Routes.walletGroupDescription, arguments: selected!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
|
||||
class WalletGroupDescriptionPage extends BasePage {
|
||||
WalletGroupDescriptionPage({required this.selectedWalletType});
|
||||
|
||||
final WalletType selectedWalletType;
|
||||
|
||||
@override
|
||||
String get title => S.current.wallet_group;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Column(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/wallet_group.png',
|
||||
scale: 0.8,
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(text: '${S.of(context).wallet_group_description_one} '),
|
||||
TextSpan(
|
||||
text: '${S.of(context).wallet_group.toLowerCase()} ',
|
||||
style: TextStyle(fontWeight: FontWeight.w700),
|
||||
),
|
||||
TextSpan(
|
||||
text: '${S.of(context).wallet_group_description_two} ',
|
||||
),
|
||||
TextSpan(
|
||||
text: '${S.of(context).choose_wallet_group} ',
|
||||
style: TextStyle(fontWeight: FontWeight.w700),
|
||||
),
|
||||
TextSpan(
|
||||
text: '${S.of(context).wallet_group_description_three} ',
|
||||
),
|
||||
TextSpan(
|
||||
text: '${S.of(context).create_new_seed} ',
|
||||
style: TextStyle(fontWeight: FontWeight.w700),
|
||||
),
|
||||
TextSpan(text: S.of(context).wallet_group_description_four),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
height: 1.5,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
PrimaryButton(
|
||||
onPressed: () => Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: selectedWalletType),
|
||||
),
|
||||
text: S.of(context).create_new_seed,
|
||||
color: Theme.of(context).cardColor,
|
||||
textColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
PrimaryButton(
|
||||
onPressed: () => Navigator.of(context).pushNamed(
|
||||
Routes.walletGroupsDisplayPage,
|
||||
arguments: selectedWalletType,
|
||||
),
|
||||
text: S.of(context).choose_wallet_group,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_groups_display_view_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
import '../../../themes/extensions/cake_text_theme.dart';
|
||||
|
||||
class WalletGroupsDisplayPage extends BasePage {
|
||||
WalletGroupsDisplayPage(this.walletGroupsDisplayViewModel);
|
||||
|
||||
final WalletGroupsDisplayViewModel walletGroupsDisplayViewModel;
|
||||
|
||||
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
|
||||
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
|
||||
|
||||
@override
|
||||
String get title => S.current.wallet_group;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletGroupsDisplayBody(
|
||||
walletGroupsDisplayViewModel: walletGroupsDisplayViewModel,
|
||||
);
|
||||
}
|
||||
|
||||
class WalletGroupsDisplayBody extends StatelessWidget {
|
||||
WalletGroupsDisplayBody({required this.walletGroupsDisplayViewModel});
|
||||
|
||||
final WalletGroupsDisplayViewModel walletGroupsDisplayViewModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Observer(
|
||||
builder: (context) {
|
||||
return Column(
|
||||
children: [
|
||||
if (walletGroupsDisplayViewModel.hasNoFilteredWallet) ...{
|
||||
WalletGroupEmptyStateWidget(),
|
||||
},
|
||||
...walletGroupsDisplayViewModel.multiWalletGroups.map(
|
||||
(walletGroup) {
|
||||
return Observer(builder: (context) {
|
||||
final index = walletGroupsDisplayViewModel.multiWalletGroups
|
||||
.indexOf(walletGroup);
|
||||
final group = walletGroupsDisplayViewModel.multiWalletGroups[index];
|
||||
final groupName =
|
||||
group.groupName ?? '${S.of(context).wallet_group} ${index + 1}';
|
||||
return GroupedWalletExpansionTile(
|
||||
leadingWidget:
|
||||
Icon(Icons.account_balance_wallet_outlined, size: 28),
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
title: groupName,
|
||||
childWallets: group.wallets.map((walletInfo) {
|
||||
return walletGroupsDisplayViewModel
|
||||
.convertWalletInfoToWalletListItem(walletInfo);
|
||||
}).toList(),
|
||||
isSelected:
|
||||
walletGroupsDisplayViewModel.selectedWalletGroup == group,
|
||||
onTitleTapped: () =>
|
||||
walletGroupsDisplayViewModel.selectWalletGroup(group),
|
||||
onChildItemTapped: (_) =>
|
||||
walletGroupsDisplayViewModel.selectWalletGroup(group),
|
||||
);
|
||||
});
|
||||
},
|
||||
).toList(),
|
||||
...walletGroupsDisplayViewModel.singleWalletsList.map((singleWallet) {
|
||||
return Observer(
|
||||
builder: (context) {
|
||||
final index = walletGroupsDisplayViewModel.singleWalletsList
|
||||
.indexOf(singleWallet);
|
||||
final wallet = walletGroupsDisplayViewModel.singleWalletsList[index];
|
||||
return GroupedWalletExpansionTile(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
title: wallet.name,
|
||||
isSelected:
|
||||
walletGroupsDisplayViewModel.selectedSingleWallet == wallet,
|
||||
leadingWidget: Image.asset(
|
||||
walletTypeToCryptoCurrency(wallet.type).iconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
onTitleTapped: () =>
|
||||
walletGroupsDisplayViewModel.selectSingleWallet(wallet),
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return LoadingPrimaryButton(
|
||||
isLoading: walletGroupsDisplayViewModel.isFetchingMnemonic,
|
||||
onPressed: () {
|
||||
if (walletGroupsDisplayViewModel.hasNoFilteredWallet) {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: walletGroupsDisplayViewModel.type),
|
||||
);
|
||||
} else {
|
||||
onTypeSelected(context);
|
||||
}
|
||||
},
|
||||
text: walletGroupsDisplayViewModel.hasNoFilteredWallet
|
||||
? S.of(context).create_new_seed
|
||||
: S.of(context).seed_language_next,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white,
|
||||
isDisabled: !walletGroupsDisplayViewModel.hasNoFilteredWallet
|
||||
? (walletGroupsDisplayViewModel.selectedWalletGroup == null &&
|
||||
walletGroupsDisplayViewModel.selectedSingleWallet == null)
|
||||
: false,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onTypeSelected(BuildContext context) async {
|
||||
final mnemonic = await walletGroupsDisplayViewModel.getSelectedWalletMnemonic();
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(
|
||||
type: walletGroupsDisplayViewModel.type,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: walletGroupsDisplayViewModel.parentAddress,
|
||||
isChildWallet: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class WalletGroupEmptyStateWidget extends StatelessWidget {
|
||||
const WalletGroupEmptyStateWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/wallet_group.png',
|
||||
scale: 0.8,
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${S.of(context).wallet_group_empty_state_text_one} ',
|
||||
),
|
||||
TextSpan(
|
||||
text: '${S.of(context).create_new_seed} ',
|
||||
style: TextStyle(fontWeight: FontWeight.w700),
|
||||
),
|
||||
TextSpan(text: S.of(context).wallet_group_empty_state_text_two),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
height: 1.5,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GroupedWalletExpansionTile extends StatelessWidget {
|
||||
GroupedWalletExpansionTile({
|
||||
required this.title,
|
||||
required this.isSelected,
|
||||
this.childWallets = const [],
|
||||
this.onTitleTapped,
|
||||
this.onChildItemTapped = _defaultVoidCallback,
|
||||
this.leadingWidget,
|
||||
this.trailingWidget,
|
||||
this.childTrailingWidget,
|
||||
this.decoration,
|
||||
this.color,
|
||||
this.textColor,
|
||||
this.arrowColor,
|
||||
this.borderRadius,
|
||||
this.margin,
|
||||
this.tileKey,
|
||||
}) : super(key: tileKey);
|
||||
|
||||
final Key? tileKey;
|
||||
final bool isSelected;
|
||||
|
||||
final VoidCallback? onTitleTapped;
|
||||
final void Function(WalletListItem item) onChildItemTapped;
|
||||
|
||||
final String title;
|
||||
final Widget? leadingWidget;
|
||||
final Widget? trailingWidget;
|
||||
final Widget Function(WalletListItem)? childTrailingWidget;
|
||||
|
||||
final List<WalletListItem> childWallets;
|
||||
|
||||
final Color? color;
|
||||
final Color? textColor;
|
||||
final Color? arrowColor;
|
||||
final EdgeInsets? margin;
|
||||
final Decoration? decoration;
|
||||
final BorderRadius? borderRadius;
|
||||
|
||||
static void _defaultVoidCallback(WalletListItem ITEM) {}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backgroundColor = color ?? (isSelected ? Colors.green : Theme.of(context).cardColor);
|
||||
final effectiveTextColor = textColor ??
|
||||
(isSelected
|
||||
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
||||
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor);
|
||||
|
||||
final effectiveArrowColor = arrowColor ??
|
||||
(isSelected
|
||||
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
||||
: Theme.of(context).extension<FilterTheme>()!.titlesColor);
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: borderRadius ?? BorderRadius.all(Radius.circular(30)),
|
||||
color: backgroundColor,
|
||||
),
|
||||
margin: margin ?? const EdgeInsets.only(bottom: 12.0),
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
dividerColor: Colors.transparent,
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
),
|
||||
child: ExpansionTile(
|
||||
key: tileKey,
|
||||
tilePadding: EdgeInsets.symmetric(vertical: 1, horizontal: 16),
|
||||
iconColor: effectiveArrowColor,
|
||||
collapsedIconColor: effectiveArrowColor,
|
||||
leading: leadingWidget,
|
||||
trailing: trailingWidget ?? (childWallets.isEmpty ? SizedBox.shrink() : null),
|
||||
title: GestureDetector(
|
||||
onTap: onTitleTapped,
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: effectiveTextColor,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
children: childWallets.map(
|
||||
(item) {
|
||||
final walletTypeToCrypto = walletTypeToCryptoCurrency(item.type);
|
||||
return ListTile(
|
||||
key: ValueKey(item.name),
|
||||
trailing: childTrailingWidget?.call(item),
|
||||
onTap: () => onChildItemTapped(item),
|
||||
leading: Image.asset(
|
||||
walletTypeToCrypto.iconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
title: Text(
|
||||
item.name,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: effectiveTextColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,11 @@ class SelectButton extends StatelessWidget {
|
||||
this.arrowColor,
|
||||
this.borderColor,
|
||||
this.deviceConnectionTypes,
|
||||
this.borderRadius,
|
||||
this.padding,
|
||||
});
|
||||
|
||||
final Image? image;
|
||||
final Widget? image;
|
||||
final String text;
|
||||
final double textSize;
|
||||
final bool isSelected;
|
||||
@@ -32,6 +34,8 @@ class SelectButton extends StatelessWidget {
|
||||
final Color? textColor;
|
||||
final Color? arrowColor;
|
||||
final Color? borderColor;
|
||||
final BorderRadius? borderRadius;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -62,10 +66,10 @@ class SelectButton extends StatelessWidget {
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: height,
|
||||
padding: EdgeInsets.only(left: 30, right: 30),
|
||||
padding: padding ?? EdgeInsets.only(left: 30, right: 30),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
borderRadius: borderRadius ?? BorderRadius.all(Radius.circular(30)),
|
||||
color: backgroundColor,
|
||||
border: borderColor != null ? Border.all(color: borderColor!) : null,
|
||||
),
|
||||
|
||||
@@ -281,16 +281,9 @@ class WalletRestorePage extends BasePage {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((walletRestoreViewModel.type == WalletType.litecoin) &&
|
||||
(seedWords.length != WalletRestoreViewModelBase.electrumSeedMnemonicLength &&
|
||||
seedWords.length != WalletRestoreViewModelBase.electrumShortSeedMnemonicLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// bip39:
|
||||
const validSeedLengths = [12, 18, 24];
|
||||
if (walletRestoreViewModel.type == WalletType.bitcoin &&
|
||||
!(validSeedLengths.contains(seedWords.length))) {
|
||||
if (!(validSeedLengths.contains(seedWords.length))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import 'package:another_flushbar/flushbar.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
||||
import 'package:cake_wallet/palette.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/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.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/store/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
@@ -22,29 +18,29 @@ import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
|
||||
class WalletEditPage extends BasePage {
|
||||
WalletEditPage(
|
||||
{required this.walletEditViewModel,
|
||||
required this.editingWallet,
|
||||
required this.walletNewVM,
|
||||
required this.authService})
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
WalletEditPage({
|
||||
required this.pageArguments,
|
||||
}) : _formKey = GlobalKey<FormState>(),
|
||||
_labelController = TextEditingController(),
|
||||
walletEditViewModel = pageArguments.walletEditViewModel!,
|
||||
super() {
|
||||
_labelController.text = editingWallet.name;
|
||||
_labelController.text =
|
||||
pageArguments.isWalletGroup ? pageArguments.groupName : pageArguments.editingWallet.name;
|
||||
_labelController.addListener(() => walletEditViewModel.newName = _labelController.text);
|
||||
}
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _labelController;
|
||||
|
||||
final WalletEditPageArguments pageArguments;
|
||||
final WalletEditViewModel walletEditViewModel;
|
||||
final WalletNewVM walletNewVM;
|
||||
final WalletListItem editingWallet;
|
||||
final AuthService authService;
|
||||
|
||||
@override
|
||||
String get title => S.current.wallet_list_edit_wallet;
|
||||
String get title => pageArguments.isWalletGroup
|
||||
? S.current.wallet_list_edit_group_name
|
||||
: S.current.wallet_list_edit_wallet;
|
||||
|
||||
Flushbar<void>? _progressBar;
|
||||
|
||||
@@ -57,11 +53,14 @@ class WalletEditPage extends BasePage {
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: BaseTextFormField(
|
||||
controller: _labelController,
|
||||
hintText: S.of(context).wallet_list_wallet_name,
|
||||
validator: WalletNameValidator()))),
|
||||
child: Center(
|
||||
child: BaseTextFormField(
|
||||
controller: _labelController,
|
||||
hintText: S.of(context).wallet_list_wallet_name,
|
||||
validator: WalletNameValidator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
final isLoading = walletEditViewModel.state is WalletEditRenamePending ||
|
||||
@@ -69,24 +68,26 @@ class WalletEditPage extends BasePage {
|
||||
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
isDisabled: isLoading,
|
||||
onPressed: () => _removeWallet(context),
|
||||
text: S.of(context).delete,
|
||||
color: Palette.red,
|
||||
textColor: Colors.white),
|
||||
if (!pageArguments.isWalletGroup)
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
isDisabled: isLoading,
|
||||
onPressed: () => _removeWallet(context),
|
||||
text: S.of(context).delete,
|
||||
color: Palette.red,
|
||||
textColor: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState?.validate() ?? false) {
|
||||
if (walletNewVM.nameExists(walletEditViewModel.newName)) {
|
||||
if (pageArguments.walletNewVM!
|
||||
.nameExists(walletEditViewModel.newName)) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
@@ -102,29 +103,33 @@ class WalletEditPage extends BasePage {
|
||||
try {
|
||||
bool confirmed = false;
|
||||
|
||||
if (SettingsStoreBase
|
||||
.walletPasswordDirectInput) {
|
||||
if (SettingsStoreBase.walletPasswordDirectInput) {
|
||||
await Navigator.of(context).pushNamed(
|
||||
Routes.walletUnlockLoadable,
|
||||
arguments: WalletUnlockArguments(
|
||||
authPasswordHandler:
|
||||
(String password) async {
|
||||
await walletEditViewModel
|
||||
.changeName(editingWallet,
|
||||
password: password);
|
||||
authPasswordHandler: (String password) async {
|
||||
await walletEditViewModel.changeName(
|
||||
pageArguments.editingWallet,
|
||||
password: password,
|
||||
isWalletGroup: pageArguments.isWalletGroup,
|
||||
groupParentAddress: pageArguments.parentAddress,
|
||||
);
|
||||
},
|
||||
callback: (bool
|
||||
isAuthenticatedSuccessfully,
|
||||
callback: (bool isAuthenticatedSuccessfully,
|
||||
AuthPageState auth) async {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
auth.close();
|
||||
confirmed = true;
|
||||
}
|
||||
},
|
||||
walletName: editingWallet.name,
|
||||
walletType: editingWallet.type));
|
||||
walletName: pageArguments.editingWallet.name,
|
||||
walletType: pageArguments.editingWallet.type));
|
||||
} else {
|
||||
await walletEditViewModel.changeName(editingWallet);
|
||||
await walletEditViewModel.changeName(
|
||||
pageArguments.editingWallet,
|
||||
isWalletGroup: pageArguments.isWalletGroup,
|
||||
groupParentAddress: pageArguments.parentAddress,
|
||||
);
|
||||
confirmed = true;
|
||||
}
|
||||
|
||||
@@ -154,7 +159,9 @@ class WalletEditPage extends BasePage {
|
||||
}
|
||||
|
||||
Future<void> _removeWallet(BuildContext context) async {
|
||||
authService.authenticateAction(context, onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
pageArguments.authService!.authenticateAction(
|
||||
context,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
@@ -173,7 +180,8 @@ class WalletEditPage extends BasePage {
|
||||
builder: (BuildContext dialogContext) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).delete_wallet,
|
||||
alertContent: S.of(context).delete_wallet_confirm_message(editingWallet.name),
|
||||
alertContent:
|
||||
S.of(context).delete_wallet_confirm_message(pageArguments.editingWallet.name),
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).delete,
|
||||
actionLeftButton: () => Navigator.of(dialogContext).pop(),
|
||||
@@ -187,13 +195,16 @@ class WalletEditPage extends BasePage {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
try {
|
||||
changeProcessText(context, S.of(context).wallet_list_removing_wallet(editingWallet.name));
|
||||
await walletEditViewModel.remove(editingWallet);
|
||||
changeProcessText(
|
||||
context, S.of(context).wallet_list_removing_wallet(pageArguments.editingWallet.name));
|
||||
await walletEditViewModel.remove(pageArguments.editingWallet);
|
||||
hideProgressText();
|
||||
} catch (e) {
|
||||
changeProcessText(
|
||||
context,
|
||||
S.of(context).wallet_list_failed_to_remove(editingWallet.name, e.toString()),
|
||||
S
|
||||
.of(context)
|
||||
.wallet_list_failed_to_remove(pageArguments.editingWallet.name, e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class EditWalletButtonWidget extends StatelessWidget {
|
||||
const EditWalletButtonWidget({
|
||||
required this.width,
|
||||
required this.onTap,
|
||||
this.isGroup = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final bool isGroup;
|
||||
final double width;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: width,
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Center(
|
||||
child: Container(
|
||||
height: 40,
|
||||
width: 44,
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsBackgroundColor,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.edit,
|
||||
size: 14,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isGroup) ...{
|
||||
SizedBox(width: 6),
|
||||
Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
size: 24,
|
||||
color: Theme.of(context).extension<FilterTheme>()!.titlesColor,
|
||||
),
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,13 @@ class FilteredList extends StatefulWidget {
|
||||
required this.list,
|
||||
required this.itemBuilder,
|
||||
required this.updateFunction,
|
||||
this.shrinkWrap = false,
|
||||
});
|
||||
|
||||
final ObservableList<dynamic> list;
|
||||
final Widget Function(BuildContext, int) itemBuilder;
|
||||
final Function updateFunction;
|
||||
final bool shrinkWrap;
|
||||
|
||||
@override
|
||||
FilteredListState createState() => FilteredListState();
|
||||
@@ -22,6 +24,7 @@ class FilteredListState extends State<FilteredList> {
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(
|
||||
builder: (_) => ReorderableListView.builder(
|
||||
shrinkWrap: widget.shrinkWrap,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemBuilder: widget.itemBuilder,
|
||||
itemCount: widget.list.length,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
||||
import 'package:cake_wallet/entities/wallet_list_order_types.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_list_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_list/edit_wallet_button_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_list/filtered_list.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
@@ -7,8 +11,6 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
@@ -23,7 +25,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
|
||||
|
||||
class WalletListPage extends BasePage {
|
||||
WalletListPage({required this.walletListViewModel, required this.authService});
|
||||
@@ -128,112 +129,143 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: Observer(
|
||||
builder: (_) => FilteredList(
|
||||
list: widget.walletListViewModel.wallets,
|
||||
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
|
||||
itemBuilder: (__, index) {
|
||||
final wallet = widget.walletListViewModel.wallets[index];
|
||||
final currentColor = wallet.isCurrent
|
||||
? Theme.of(context)
|
||||
.extension<WalletListTheme>()!
|
||||
.createNewWalletButtonBackgroundColor
|
||||
: Theme.of(context).colorScheme.background;
|
||||
final row = GestureDetector(
|
||||
key: ValueKey(wallet.name),
|
||||
onTap: () => wallet.isCurrent ? null : _loadWallet(wallet),
|
||||
child: Container(
|
||||
height: tileHeight,
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: tileHeight,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(4),
|
||||
bottomRight: Radius.circular(4)),
|
||||
color: currentColor),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: tileHeight,
|
||||
padding: EdgeInsets.only(left: 20, right: 20),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
wallet.isEnabled
|
||||
? _imageFor(
|
||||
type: wallet.type,
|
||||
isTestnet: wallet.isTestnet,
|
||||
)
|
||||
: nonWalletTypeIcon,
|
||||
SizedBox(width: 10),
|
||||
Flexible(
|
||||
child: Text(
|
||||
wallet.name,
|
||||
maxLines: null,
|
||||
softWrap: true,
|
||||
style: TextStyle(
|
||||
fontSize: DeviceInfo.instance.isDesktop ? 18 : 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.titleColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.walletListViewModel.multiWalletGroups.isNotEmpty) ...{
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Text(
|
||||
S.current.shared_seed_wallet_groups,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return wallet.isCurrent
|
||||
? row
|
||||
: Row(
|
||||
key: ValueKey(wallet.name),
|
||||
children: [
|
||||
Expanded(child: row),
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.walletEdit,
|
||||
arguments: [widget.walletListViewModel, wallet]),
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(
|
||||
right: DeviceInfo.instance.isMobile ? 20 : 40),
|
||||
child: Center(
|
||||
child: Container(
|
||||
height: 40,
|
||||
width: 44,
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.iconsBackgroundColor,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.edit,
|
||||
size: 14,
|
||||
color: Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.iconsColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
child: Observer(
|
||||
builder: (_) => FilteredList(
|
||||
shrinkWrap: true,
|
||||
list: widget.walletListViewModel.multiWalletGroups,
|
||||
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
|
||||
itemBuilder: (context, index) {
|
||||
final group = widget.walletListViewModel.multiWalletGroups[index];
|
||||
final groupName = group.groupName ??
|
||||
'${S.current.wallet_group} ${index + 1}';
|
||||
return GroupedWalletExpansionTile(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
margin: EdgeInsets.only(left: 20, right: 20, bottom: 12),
|
||||
title: groupName,
|
||||
tileKey: ValueKey('group_wallets_expansion_tile_widget_$index'),
|
||||
leadingWidget: Icon(
|
||||
Icons.account_balance_wallet_outlined,
|
||||
size: 28,
|
||||
),
|
||||
],
|
||||
);
|
||||
trailingWidget: EditWalletButtonWidget(
|
||||
width: 74,
|
||||
isGroup: true,
|
||||
onTap: () {
|
||||
final wallet = widget.walletListViewModel
|
||||
.convertWalletInfoToWalletListItem(group.wallets.first);
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: wallet,
|
||||
isWalletGroup: true,
|
||||
groupName: groupName,
|
||||
parentAddress: group.parentAddress,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
childWallets: group.wallets.map((walletInfo) {
|
||||
return widget.walletListViewModel
|
||||
.convertWalletInfoToWalletListItem(walletInfo);
|
||||
}).toList(),
|
||||
isSelected: false,
|
||||
onChildItemTapped: (wallet) =>
|
||||
wallet.isCurrent ? null : _loadWallet(wallet),
|
||||
childTrailingWidget: (item) {
|
||||
return item.isCurrent
|
||||
? SizedBox.shrink()
|
||||
: EditWalletButtonWidget(
|
||||
width: 44,
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: item,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
},
|
||||
),
|
||||
if (widget.walletListViewModel.singleWalletsList.isNotEmpty) ...{
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Text(
|
||||
S.current.single_seed_wallets_group,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
child: Observer(
|
||||
builder: (_) => FilteredList(
|
||||
shrinkWrap: true,
|
||||
list: widget.walletListViewModel.singleWalletsList,
|
||||
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
|
||||
itemBuilder: (context, index) {
|
||||
final wallet = widget.walletListViewModel.singleWalletsList[index];
|
||||
|
||||
return GroupedWalletExpansionTile(
|
||||
tileKey: ValueKey('single_wallets_expansion_tile_widget_$index'),
|
||||
leadingWidget: Image.asset(
|
||||
walletTypeToCryptoCurrency(wallet.type).iconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
title: wallet.name,
|
||||
isSelected: false,
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
margin: EdgeInsets.only(left: 20, right: 20, bottom: 12),
|
||||
onTitleTapped: () => wallet.isCurrent ? null : _loadWallet(wallet),
|
||||
trailingWidget: wallet.isCurrent
|
||||
? null
|
||||
: EditWalletButtonWidget(
|
||||
width: 44,
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: wallet,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -249,14 +281,18 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
widget.authService.authenticateAction(
|
||||
context,
|
||||
route: Routes.newWallet,
|
||||
arguments: widget.walletListViewModel.currentWalletType,
|
||||
arguments: NewWalletArguments(
|
||||
type: widget.walletListViewModel.currentWalletType,
|
||||
),
|
||||
conditionToDetermineIfToUse2FA:
|
||||
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: widget.walletListViewModel.currentWalletType,
|
||||
arguments: NewWalletArguments(
|
||||
type: widget.walletListViewModel.currentWalletType,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -340,15 +376,15 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
|
||||
Future<void> _loadWallet(WalletListItem wallet) async {
|
||||
if (SettingsStoreBase.walletPasswordDirectInput) {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletUnlockLoadable,
|
||||
Navigator.of(context).pushNamed(Routes.walletUnlockLoadable,
|
||||
arguments: WalletUnlockArguments(
|
||||
callback: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
auth.close();
|
||||
setState(() {});
|
||||
}
|
||||
}, walletName: wallet.name,
|
||||
},
|
||||
walletName: wallet.name,
|
||||
walletType: wallet.type));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ abstract class SettingsStoreBase with Store {
|
||||
required AutoGenerateSubaddressStatus initialAutoGenerateSubaddressStatus,
|
||||
required MoneroSeedType initialMoneroSeedType,
|
||||
required BitcoinSeedType initialBitcoinSeedType,
|
||||
required NanoSeedType initialNanoSeedType,
|
||||
required bool initialAppSecure,
|
||||
required bool initialDisableBuy,
|
||||
required bool initialDisableSell,
|
||||
@@ -132,6 +133,7 @@ abstract class SettingsStoreBase with Store {
|
||||
autoGenerateSubaddressStatus = initialAutoGenerateSubaddressStatus,
|
||||
moneroSeedType = initialMoneroSeedType,
|
||||
bitcoinSeedType = initialBitcoinSeedType,
|
||||
nanoSeedType = initialNanoSeedType,
|
||||
fiatApiMode = initialFiatMode,
|
||||
allowBiometricalAuthentication = initialAllowBiometricalAuthentication,
|
||||
selectedCake2FAPreset = initialCake2FAPresetOptions,
|
||||
@@ -341,6 +343,11 @@ abstract class SettingsStoreBase with Store {
|
||||
(BitcoinSeedType bitcoinSeedType) => sharedPreferences.setInt(
|
||||
PreferencesKey.bitcoinSeedType, bitcoinSeedType.raw));
|
||||
|
||||
reaction(
|
||||
(_) => nanoSeedType,
|
||||
(NanoSeedType nanoSeedType) =>
|
||||
sharedPreferences.setInt(PreferencesKey.nanoSeedType, nanoSeedType.raw));
|
||||
|
||||
reaction(
|
||||
(_) => fiatApiMode,
|
||||
(FiatApiMode mode) =>
|
||||
@@ -569,6 +576,7 @@ abstract class SettingsStoreBase with Store {
|
||||
static const defaultSeedPhraseLength = SeedPhraseLength.twelveWords;
|
||||
static const defaultMoneroSeedType = MoneroSeedType.defaultSeedType;
|
||||
static const defaultBitcoinSeedType = BitcoinSeedType.defaultDerivationType;
|
||||
static const defaultNanoSeedType = NanoSeedType.defaultDerivationType;
|
||||
|
||||
@observable
|
||||
FiatCurrency fiatCurrency;
|
||||
@@ -603,6 +611,9 @@ abstract class SettingsStoreBase with Store {
|
||||
@observable
|
||||
BitcoinSeedType bitcoinSeedType;
|
||||
|
||||
@observable
|
||||
NanoSeedType nanoSeedType;
|
||||
|
||||
@observable
|
||||
bool isAppSecure;
|
||||
|
||||
@@ -974,6 +985,11 @@ abstract class SettingsStoreBase with Store {
|
||||
? BitcoinSeedType.deserialize(raw: _bitcoinSeedType)
|
||||
: defaultBitcoinSeedType;
|
||||
|
||||
final _nanoSeedType = sharedPreferences.getInt(PreferencesKey.nanoSeedType);
|
||||
|
||||
final nanoSeedType =
|
||||
_nanoSeedType != null ? NanoSeedType.deserialize(raw: _nanoSeedType) : defaultNanoSeedType;
|
||||
|
||||
final nodes = <WalletType, Node>{};
|
||||
final powNodes = <WalletType, Node>{};
|
||||
|
||||
@@ -1138,6 +1154,7 @@ abstract class SettingsStoreBase with Store {
|
||||
initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus,
|
||||
initialMoneroSeedType: moneroSeedType,
|
||||
initialBitcoinSeedType: bitcoinSeedType,
|
||||
initialNanoSeedType: nanoSeedType,
|
||||
initialAppSecure: isAppSecure,
|
||||
initialDisableBuy: disableBuy,
|
||||
initialDisableSell: disableSell,
|
||||
@@ -1270,6 +1287,11 @@ abstract class SettingsStoreBase with Store {
|
||||
? BitcoinSeedType.deserialize(raw: _bitcoinSeedType)
|
||||
: defaultBitcoinSeedType;
|
||||
|
||||
final _nanoSeedType = sharedPreferences.getInt(PreferencesKey.nanoSeedType);
|
||||
|
||||
nanoSeedType =
|
||||
_nanoSeedType != null ? NanoSeedType.deserialize(raw: _nanoSeedType) : defaultNanoSeedType;
|
||||
|
||||
balanceDisplayMode = BalanceDisplayMode.deserialize(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!);
|
||||
shouldSaveRecipientAddress =
|
||||
|
||||
+11
-4
@@ -13,8 +13,15 @@ class CWTron extends Tron {
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
}) =>
|
||||
TronNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
|
||||
TronNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: parentAddress);
|
||||
|
||||
@override
|
||||
WalletCredentials createTronRestoreWalletFromSeedCredentials({
|
||||
@@ -34,7 +41,7 @@ class CWTron extends Tron {
|
||||
|
||||
@override
|
||||
String getAddress(WalletBase wallet) => (wallet as TronWallet).walletAddresses.address;
|
||||
|
||||
|
||||
Object createTronTransactionCredentials(
|
||||
List<Output> outputs, {
|
||||
required CryptoCurrency currency,
|
||||
@@ -63,10 +70,10 @@ class CWTron extends Tron {
|
||||
|
||||
@override
|
||||
Future<void> addTronToken(WalletBase wallet, CryptoCurrency token, String contractAddress) async {
|
||||
final tronToken = TronToken(
|
||||
final tronToken = TronToken(
|
||||
name: token.name,
|
||||
symbol: token.title,
|
||||
contractAddress: contractAddress,
|
||||
contractAddress: contractAddress,
|
||||
decimal: token.decimals,
|
||||
enabled: token.enabled,
|
||||
iconPath: token.iconPath,
|
||||
|
||||
@@ -46,17 +46,30 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
||||
case WalletType.litecoin:
|
||||
return _settingsStore.bitcoinSeedType == BitcoinSeedType.bip39;
|
||||
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return _settingsStore.nanoSeedType == NanoSeedType.bip39;
|
||||
|
||||
case WalletType.monero:
|
||||
case WalletType.wownero:
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool get hasSeedTypeOption => [WalletType.monero, WalletType.wownero].contains(type);
|
||||
|
||||
bool get isMoneroSeedTypeOptionsEnabled => [
|
||||
WalletType.monero,
|
||||
WalletType.wownero,
|
||||
].contains(type);
|
||||
|
||||
bool get isBitcoinSeedTypeOptionsEnabled => [
|
||||
WalletType.bitcoin,
|
||||
WalletType.litecoin,
|
||||
].contains(type);
|
||||
|
||||
bool get isNanoSeedTypeOptionsEnabled => [WalletType.nano].contains(type);
|
||||
|
||||
bool get hasPassphraseOption => [
|
||||
WalletType.bitcoin,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'new_wallet_type_view_model.g.dart';
|
||||
|
||||
class NewWalletTypeViewModel = NewWalletTypeViewModelBase with _$NewWalletTypeViewModel;
|
||||
|
||||
abstract class NewWalletTypeViewModelBase with Store {
|
||||
NewWalletTypeViewModelBase(this._walletInfoSource);
|
||||
|
||||
@computed
|
||||
bool get hasExisitingWallet => _walletInfoSource.isNotEmpty;
|
||||
|
||||
final Box<WalletInfo> _walletInfoSource;
|
||||
}
|
||||
@@ -23,6 +23,13 @@ abstract class SeedSettingsViewModelBase with Store {
|
||||
void setBitcoinSeedType(BitcoinSeedType derivationType) =>
|
||||
_appStore.settingsStore.bitcoinSeedType = derivationType;
|
||||
|
||||
@computed
|
||||
NanoSeedType get nanoSeedType => _appStore.settingsStore.nanoSeedType;
|
||||
|
||||
@action
|
||||
void setNanoSeedType(NanoSeedType derivationType) =>
|
||||
_appStore.settingsStore.nanoSeedType = derivationType;
|
||||
|
||||
@computed
|
||||
String? get passphrase => this._seedSettingsStore.passphrase;
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ abstract class WalletCreationVMBase with Store {
|
||||
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
|
||||
derivationInfo: credentials.derivationInfo ?? getDefaultCreateDerivation(),
|
||||
hardwareWalletType: credentials.hardwareWalletType,
|
||||
parentAddress: credentials.parentAddress,
|
||||
);
|
||||
|
||||
credentials.walletInfo = walletInfo;
|
||||
@@ -117,12 +118,16 @@ abstract class WalletCreationVMBase with Store {
|
||||
}
|
||||
|
||||
DerivationInfo? getDefaultCreateDerivation() {
|
||||
final useBip39 = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39;
|
||||
final useBip39ForBitcoin = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39;
|
||||
final useBip39ForNano = seedSettingsViewModel.nanoSeedType.type == DerivationType.bip39;
|
||||
switch (type) {
|
||||
case WalletType.nano:
|
||||
if (useBip39ForNano) {
|
||||
return DerivationInfo(derivationType: DerivationType.bip39);
|
||||
}
|
||||
return DerivationInfo(derivationType: DerivationType.nano);
|
||||
case WalletType.bitcoin:
|
||||
if (useBip39) {
|
||||
if (useBip39ForBitcoin) {
|
||||
return DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/0'/0'",
|
||||
@@ -132,7 +137,7 @@ abstract class WalletCreationVMBase with Store {
|
||||
}
|
||||
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
|
||||
case WalletType.litecoin:
|
||||
if (useBip39) {
|
||||
if (useBip39ForBitcoin) {
|
||||
return DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/2'/0'",
|
||||
@@ -148,9 +153,13 @@ abstract class WalletCreationVMBase with Store {
|
||||
|
||||
DerivationInfo? getCommonRestoreDerivation() {
|
||||
final useElectrum = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.electrum;
|
||||
final useNanoStandard = seedSettingsViewModel.nanoSeedType.type == DerivationType.nano;
|
||||
switch (this.type) {
|
||||
case WalletType.nano:
|
||||
return DerivationInfo(derivationType: DerivationType.nano);
|
||||
if (useNanoStandard) {
|
||||
return DerivationInfo(derivationType: DerivationType.nano);
|
||||
}
|
||||
return DerivationInfo(derivationType: DerivationType.bip39);
|
||||
case WalletType.bitcoin:
|
||||
if (useElectrum) {
|
||||
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||
import 'package:cake_wallet/entities/wallet_group.dart';
|
||||
import 'package:cake_wallet/entities/wallet_manager.dart';
|
||||
import 'package:cake_wallet/reactions/bip39_wallet_utils.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_types.g.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'wallet_groups_display_view_model.g.dart';
|
||||
|
||||
class WalletGroupsDisplayViewModel = WalletGroupsDisplayViewModelBase
|
||||
with _$WalletGroupsDisplayViewModel;
|
||||
|
||||
abstract class WalletGroupsDisplayViewModelBase with Store {
|
||||
WalletGroupsDisplayViewModelBase(
|
||||
this._appStore,
|
||||
this._walletLoadingService,
|
||||
this._walletManager,
|
||||
this.walletListViewModel, {
|
||||
required this.type,
|
||||
}) : isFetchingMnemonic = false,
|
||||
multiWalletGroups = ObservableList<WalletGroup>(),
|
||||
singleWalletsList = ObservableList<WalletInfo>() {
|
||||
reaction((_) => _appStore.wallet, (_) => updateWalletInfoSourceList());
|
||||
updateWalletInfoSourceList();
|
||||
}
|
||||
|
||||
final WalletType type;
|
||||
final AppStore _appStore;
|
||||
final WalletManager _walletManager;
|
||||
final WalletLoadingService _walletLoadingService;
|
||||
final WalletListViewModel walletListViewModel;
|
||||
|
||||
@observable
|
||||
ObservableList<WalletGroup> multiWalletGroups;
|
||||
|
||||
@observable
|
||||
ObservableList<WalletInfo> singleWalletsList;
|
||||
|
||||
@observable
|
||||
WalletGroup? selectedWalletGroup;
|
||||
|
||||
@observable
|
||||
WalletInfo? selectedSingleWallet;
|
||||
|
||||
@observable
|
||||
String? parentAddress;
|
||||
|
||||
@observable
|
||||
bool isFetchingMnemonic;
|
||||
|
||||
@computed
|
||||
bool get hasNoFilteredWallet {
|
||||
return singleWalletsList.isEmpty && multiWalletGroups.isEmpty;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<String?> getSelectedWalletMnemonic() async {
|
||||
WalletListItem walletToUse;
|
||||
|
||||
bool isGroupSelected = selectedWalletGroup != null;
|
||||
|
||||
if (isGroupSelected) {
|
||||
walletToUse = convertWalletInfoToWalletListItem(selectedWalletGroup!.wallets.first);
|
||||
} else {
|
||||
walletToUse = convertWalletInfoToWalletListItem(selectedSingleWallet!);
|
||||
}
|
||||
|
||||
try {
|
||||
isFetchingMnemonic = true;
|
||||
final wallet = await _walletLoadingService.load(
|
||||
walletToUse.type,
|
||||
walletToUse.name,
|
||||
);
|
||||
|
||||
parentAddress =
|
||||
isGroupSelected ? selectedWalletGroup!.parentAddress : selectedSingleWallet!.address;
|
||||
|
||||
return wallet.seed;
|
||||
} catch (e) {
|
||||
return null;
|
||||
} finally {
|
||||
isFetchingMnemonic = false;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void selectWalletGroup(WalletGroup walletGroup) {
|
||||
selectedWalletGroup = walletGroup;
|
||||
selectedSingleWallet = null;
|
||||
}
|
||||
|
||||
@action
|
||||
void selectSingleWallet(WalletInfo singleWallet) {
|
||||
selectedSingleWallet = singleWallet;
|
||||
selectedWalletGroup = null;
|
||||
}
|
||||
|
||||
@action
|
||||
void updateWalletInfoSourceList() {
|
||||
List<WalletGroup> wallets = [];
|
||||
|
||||
multiWalletGroups.clear();
|
||||
singleWalletsList.clear();
|
||||
|
||||
_walletManager.updateWalletGroups();
|
||||
|
||||
final walletGroups = _walletManager.walletGroups;
|
||||
|
||||
// Iterate through the wallet groups to filter and categorize wallets
|
||||
for (var group in walletGroups) {
|
||||
// Handle group wallet filtering
|
||||
bool shouldExcludeGroup = group.wallets.any((wallet) {
|
||||
// Check for non-BIP39 wallet types
|
||||
bool isNonBIP39Wallet = !isBIP39Wallet(wallet.type);
|
||||
|
||||
// Check for nano derivation type
|
||||
bool isNanoDerivationType = wallet.type == WalletType.nano &&
|
||||
wallet.derivationInfo?.derivationType == DerivationType.nano;
|
||||
|
||||
// Check for electrum derivation type
|
||||
bool isElectrumDerivationType =
|
||||
(wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) &&
|
||||
wallet.derivationInfo?.derivationType == DerivationType.electrum;
|
||||
|
||||
// Check that selected wallet type is not present already in group
|
||||
bool isSameTypeAsSelectedWallet = wallet.type == type;
|
||||
|
||||
// Exclude if any of these conditions are true
|
||||
return isNonBIP39Wallet ||
|
||||
isNanoDerivationType ||
|
||||
isElectrumDerivationType ||
|
||||
isSameTypeAsSelectedWallet;
|
||||
});
|
||||
|
||||
if (shouldExcludeGroup) continue;
|
||||
|
||||
// If the group passes the filters, add it to the wallets list
|
||||
wallets.add(group);
|
||||
}
|
||||
|
||||
for (var group in wallets) {
|
||||
if (group.wallets.length == 1) {
|
||||
singleWalletsList.add(group.wallets.first);
|
||||
} else {
|
||||
multiWalletGroups.add(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WalletListItem convertWalletInfoToWalletListItem(WalletInfo info) {
|
||||
return WalletListItem(
|
||||
name: info.name,
|
||||
type: info.type,
|
||||
key: info.key,
|
||||
isCurrent: info.name == _appStore.wallet?.name && info.type == _appStore.wallet?.type,
|
||||
isEnabled: availableWalletTypes.contains(info.type),
|
||||
isTestnet: info.network?.toLowerCase().contains('testnet') ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||
import 'package:cake_wallet/entities/wallet_manager.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
@@ -18,8 +19,11 @@ class WalletEditRenamePending extends WalletEditViewModelState {}
|
||||
class WalletEditDeletePending extends WalletEditViewModelState {}
|
||||
|
||||
abstract class WalletEditViewModelBase with Store {
|
||||
WalletEditViewModelBase(this._walletListViewModel, this._walletLoadingService)
|
||||
: state = WalletEditViewModelInitialState(),
|
||||
WalletEditViewModelBase(
|
||||
this._walletListViewModel,
|
||||
this._walletLoadingService,
|
||||
this._walletManager,
|
||||
) : state = WalletEditViewModelInitialState(),
|
||||
newName = '';
|
||||
|
||||
@observable
|
||||
@@ -30,13 +34,30 @@ abstract class WalletEditViewModelBase with Store {
|
||||
|
||||
final WalletListViewModel _walletListViewModel;
|
||||
final WalletLoadingService _walletLoadingService;
|
||||
final WalletManager _walletManager;
|
||||
|
||||
@action
|
||||
Future<void> changeName(WalletListItem walletItem, {String? password}) async {
|
||||
Future<void> changeName(
|
||||
WalletListItem walletItem, {
|
||||
String? password,
|
||||
String? groupParentAddress,
|
||||
bool isWalletGroup = false,
|
||||
}) async {
|
||||
state = WalletEditRenamePending();
|
||||
await _walletLoadingService.renameWallet(
|
||||
walletItem.type, walletItem.name, newName,
|
||||
password: password);
|
||||
|
||||
if (isWalletGroup) {
|
||||
_walletManager.updateWalletGroups();
|
||||
|
||||
_walletManager.setGroupName(groupParentAddress!, newName);
|
||||
} else {
|
||||
await _walletLoadingService.renameWallet(
|
||||
walletItem.type,
|
||||
walletItem.name,
|
||||
newName,
|
||||
password: password,
|
||||
);
|
||||
}
|
||||
|
||||
_walletListViewModel.updateList();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||
import 'package:cake_wallet/entities/wallet_group.dart';
|
||||
import 'package:cake_wallet/entities/wallet_list_order_types.dart';
|
||||
import 'package:cake_wallet/entities/wallet_manager.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
@@ -17,7 +19,10 @@ abstract class WalletListViewModelBase with Store {
|
||||
this._walletInfoSource,
|
||||
this._appStore,
|
||||
this._walletLoadingService,
|
||||
) : wallets = ObservableList<WalletListItem>() {
|
||||
this._walletManager,
|
||||
) : wallets = ObservableList<WalletListItem>(),
|
||||
multiWalletGroups = ObservableList<WalletGroup>(),
|
||||
singleWalletsList = ObservableList<WalletListItem>() {
|
||||
setOrderType(_appStore.settingsStore.walletListOrder);
|
||||
reaction((_) => _appStore.wallet, (_) => updateList());
|
||||
updateList();
|
||||
@@ -26,6 +31,15 @@ abstract class WalletListViewModelBase with Store {
|
||||
@observable
|
||||
ObservableList<WalletListItem> wallets;
|
||||
|
||||
// @observable
|
||||
// ObservableList<WalletGroup> walletGroups;
|
||||
|
||||
@observable
|
||||
ObservableList<WalletGroup> multiWalletGroups;
|
||||
|
||||
@observable
|
||||
ObservableList<WalletListItem> singleWalletsList;
|
||||
|
||||
@computed
|
||||
bool get shouldRequireTOTP2FAForAccessingWallet =>
|
||||
_appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
|
||||
@@ -35,6 +49,7 @@ abstract class WalletListViewModelBase with Store {
|
||||
_appStore.settingsStore.shouldRequireTOTP2FAForCreatingNewWallets;
|
||||
|
||||
final AppStore _appStore;
|
||||
final WalletManager _walletManager;
|
||||
final Box<WalletInfo> _walletInfoSource;
|
||||
final WalletLoadingService _walletLoadingService;
|
||||
|
||||
@@ -53,18 +68,23 @@ abstract class WalletListViewModelBase with Store {
|
||||
@action
|
||||
void updateList() {
|
||||
wallets.clear();
|
||||
multiWalletGroups.clear();
|
||||
singleWalletsList.clear();
|
||||
|
||||
wallets.addAll(
|
||||
_walletInfoSource.values.map(
|
||||
(info) => WalletListItem(
|
||||
name: info.name,
|
||||
type: info.type,
|
||||
key: info.key,
|
||||
isCurrent: info.name == _appStore.wallet?.name && info.type == _appStore.wallet?.type,
|
||||
isEnabled: availableWalletTypes.contains(info.type),
|
||||
isTestnet: info.network?.toLowerCase().contains('testnet') ?? false,
|
||||
),
|
||||
),
|
||||
_walletInfoSource.values.map((info) => convertWalletInfoToWalletListItem(info)),
|
||||
);
|
||||
|
||||
//========== Split into shared seed groups and single wallets list
|
||||
_walletManager.updateWalletGroups();
|
||||
|
||||
for (var group in _walletManager.walletGroups) {
|
||||
if (group.wallets.length == 1) {
|
||||
singleWalletsList.add(convertWalletInfoToWalletListItem(group.wallets.first));
|
||||
} else {
|
||||
multiWalletGroups.add(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reorderAccordingToWalletList() async {
|
||||
@@ -158,4 +178,15 @@ abstract class WalletListViewModelBase with Store {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WalletListItem convertWalletInfoToWalletListItem(WalletInfo info) {
|
||||
return WalletListItem(
|
||||
name: info.name,
|
||||
type: info.type,
|
||||
key: info.key,
|
||||
isCurrent: info.name == _appStore.wallet?.name && info.type == _appStore.wallet?.type,
|
||||
isEnabled: availableWalletTypes.contains(info.type),
|
||||
isTestnet: info.network?.toLowerCase().contains('testnet') ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
||||
import 'package:cake_wallet/entities/seed_type.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/haven/haven.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
@@ -28,16 +29,17 @@ class WalletNewVM = WalletNewVMBase with _$WalletNewVM;
|
||||
|
||||
abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
WalletNewVMBase(
|
||||
AppStore appStore,
|
||||
WalletCreationService walletCreationService,
|
||||
Box<WalletInfo> walletInfoSource,
|
||||
this.advancedPrivacySettingsViewModel,
|
||||
SeedSettingsViewModel seedSettingsViewModel,
|
||||
{required WalletType type})
|
||||
: selectedMnemonicLanguage = '',
|
||||
AppStore appStore,
|
||||
WalletCreationService walletCreationService,
|
||||
Box<WalletInfo> walletInfoSource,
|
||||
this.advancedPrivacySettingsViewModel,
|
||||
SeedSettingsViewModel seedSettingsViewModel, {
|
||||
required this.newWalletArguments,
|
||||
}) : selectedMnemonicLanguage = '',
|
||||
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
|
||||
type: type, isRecovery: false);
|
||||
type: newWalletArguments!.type, isRecovery: false);
|
||||
|
||||
final NewWalletArguments? newWalletArguments;
|
||||
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
|
||||
|
||||
@observable
|
||||
@@ -62,6 +64,10 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
return seedSettingsViewModel.bitcoinSeedType == BitcoinSeedType.bip39
|
||||
? advancedPrivacySettingsViewModel.seedPhraseLength.value
|
||||
: 24;
|
||||
case WalletType.nano:
|
||||
return seedSettingsViewModel.nanoSeedType == NanoSeedType.bip39
|
||||
? advancedPrivacySettingsViewModel.seedPhraseLength.value
|
||||
: 24;
|
||||
default:
|
||||
return 24;
|
||||
}
|
||||
@@ -83,31 +89,68 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
password: walletPassword,
|
||||
isPolyseed: options.last as bool);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.createBitcoinNewWalletCredentials(
|
||||
name: name, password: walletPassword, passphrase: passphrase);
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.createBitcoinNewWalletCredentials(
|
||||
name: name, password: walletPassword, passphrase: passphrase);
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
passphrase: passphrase,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.haven:
|
||||
return haven!.createHavenNewWalletCredentials(
|
||||
name: name, language: options!.first as String, password: walletPassword);
|
||||
case WalletType.ethereum:
|
||||
return ethereum!.createEthereumNewWalletCredentials(name: name, password: walletPassword);
|
||||
return ethereum!.createEthereumNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.bitcoinCash:
|
||||
return bitcoinCash!.createBitcoinCashNewWalletCredentials(
|
||||
name: name, password: walletPassword, passphrase: passphrase);
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
passphrase: passphrase,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return nano!.createNanoNewWalletCredentials(name: name);
|
||||
return nano!.createNanoNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonNewWalletCredentials(name: name, password: walletPassword);
|
||||
return polygon!.createPolygonNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.solana:
|
||||
return solana!.createSolanaNewWalletCredentials(name: name, password: walletPassword);
|
||||
return solana!.createSolanaNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.tron:
|
||||
return tron!.createTronNewWalletCredentials(name: name);
|
||||
return tron!.createTronNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.wownero:
|
||||
return wownero!.createWowneroNewWalletCredentials(
|
||||
name: name, language: options!.first as String, isPolyseed: options.last as bool);
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
language: options!.first as String,
|
||||
isPolyseed: options.last as bool,
|
||||
);
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user