diff --git a/assets/electrum_server_list.yml b/assets/electrum_server_list.yml index 4baf56aec..0331f42ea 100644 --- a/assets/electrum_server_list.yml +++ b/assets/electrum_server_list.yml @@ -1,2 +1,4 @@ - - uri: electrum2.hodlister.co:50002 \ No newline at end of file + uri: electrum2.hodlister.co:50002 +- + uri: bitcoin.electrumx.multicoin.co:50002 \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 959c597e1..641750308 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -61,7 +61,7 @@ DEPENDENCIES: - cw_monero (from `.symlinks/plugins/cw_monero/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`) - esys_flutter_share (from `.symlinks/plugins/esys_flutter_share/ios`) - - Flutter (from `.symlinks/flutter/ios`) + - Flutter (from `.symlinks/flutter/ios-release`) - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - local_auth (from `.symlinks/plugins/local_auth/ios`) @@ -92,7 +92,7 @@ EXTERNAL SOURCES: esys_flutter_share: :path: ".symlinks/plugins/esys_flutter_share/ios" Flutter: - :path: ".symlinks/flutter/ios" + :path: ".symlinks/flutter/ios-release" flutter_plugin_android_lifecycle: :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios" flutter_secure_storage: diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 34c4e48a4..987e086fc 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -373,7 +373,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -509,7 +509,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -540,7 +540,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 0b40211a4..65000c894 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -22,6 +22,8 @@ $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS + NSCameraUsageDescription + Cake Wallet requires access to your phone’s camera. UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/lib/bitcoin/bitcoin_wallet.dart b/lib/bitcoin/bitcoin_wallet.dart index b83e95ef1..4f67fa59f 100644 --- a/lib/bitcoin/bitcoin_wallet.dart +++ b/lib/bitcoin/bitcoin_wallet.dart @@ -200,7 +200,8 @@ abstract class BitcoinWalletBase extends WalletBase with Store { Future startSync() async { try { syncStatus = StartingSyncStatus(); - transactionHistory.updateAsync(onFinished: () => print('finished!')); + transactionHistory.updateAsync( + onFinished: () => print('transactionHistory update finished!')); _subscribeForUpdates(); await _updateBalance(); syncStatus = SyncedSyncStatus(); @@ -215,11 +216,7 @@ abstract class BitcoinWalletBase extends WalletBase with Store { Future connectToNode({@required Node node}) async { try { syncStatus = ConnectingSyncStatus(); - // electrum2.hodlister.co - // bitcoin.electrumx.multicoin.co:50002 - // electrum2.taborsky.cz:5002 - await eclient.connect( - host: 'bitcoin.electrumx.multicoin.co', port: 50002); + await eclient.connectToUri(node.uri); syncStatus = ConnectedSyncStatus(); } catch (e) { print(e.toString()); diff --git a/lib/bitcoin/bitcoin_wallet_service.dart b/lib/bitcoin/bitcoin_wallet_service.dart index d5827df18..e18692968 100644 --- a/lib/bitcoin/bitcoin_wallet_service.dart +++ b/lib/bitcoin/bitcoin_wallet_service.dart @@ -49,10 +49,9 @@ class BitcoinWalletService extends WalletService< } @override - Future remove(String wallet) { - // TODO: implement remove - throw UnimplementedError(); - } + Future remove(String wallet) async => + File(await pathForWalletDir(name: wallet, type: WalletType.bitcoin)) + .delete(recursive: true); @override Future restoreFromKeys( diff --git a/lib/bitcoin/electrum.dart b/lib/bitcoin/electrum.dart index f24f6611f..d92c69405 100644 --- a/lib/bitcoin/electrum.dart +++ b/lib/bitcoin/electrum.dart @@ -37,11 +37,15 @@ class ElectrumClient { bool _isConnected; Timer _aliveTimer; - Future connect({@required String host, @required int port}) async { - if (socket != null) { - await socket.close(); - } + Future connectToUri(String uri) async { + final _uri = Uri.parse(uri); + final host = _uri.scheme; + final port = int.parse(_uri.path); + await connect(host: host, port: port); + } + Future connect({@required String host, @required int port}) async { + await socket?.close(); final start = DateTime.now(); socket = await SecureSocket.connect(host, port, timeout: connectionTimeout); diff --git a/lib/di.dart b/lib/di.dart index 4c4c038a7..d7f425edf 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -25,6 +25,7 @@ import 'package:cake_wallet/src/screens/send/send_page.dart'; import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart'; import 'package:cake_wallet/store/wallet_list_store.dart'; +import 'package:cake_wallet/utils/mobx.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart'; import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart'; @@ -74,8 +75,20 @@ NodeListStore setupNodeListStore(Box nodeSource) { _nodeListStore = NodeListStore(); _nodeListStore.replaceValues(nodeSource.values); _onNodesSourceChange = nodeSource.watch(); - _onNodesSourceChange - .listen((_) => _nodeListStore.replaceValues(nodeSource.values)); + _onNodesSourceChange.listen((event) { +// print(event); + + if (event.deleted) { + _nodeListStore.nodes.removeWhere((n) { + return n.key != null ? n.key == event.key : true; + }); + } + + if (event.value is Node) { + final val = event.value as Node; + _nodeListStore.nodes.add(val); + } + }); return _nodeListStore; } @@ -274,10 +287,11 @@ Future setup( getIt.registerFactoryParam((Contact contact, _) => ContactPage(getIt.get(param1: contact))); - getIt.registerFactory(() => NodeListViewModel( - getIt.get().nodeListStore, - nodeSource, - getIt.get().wallet)); + getIt.registerFactory(() { + final appStore = getIt.get(); + return NodeListViewModel(appStore.nodeListStore, nodeSource, + appStore.wallet, appStore.settingsStore); + }); getIt.registerFactory(() => NodeListPage(getIt.get())); diff --git a/lib/main.dart b/lib/main.dart index d1be6866d..6e69f4410 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -127,7 +127,7 @@ void main() async { contactSource: contacts, tradesSource: trades, fiatConvertationService: fiatConvertationService, - initialMigrationVersion: 3); + initialMigrationVersion: 4); setReactions( settingsStore: settingsStore, @@ -169,7 +169,7 @@ Future initialSetup( @required Box contactSource, @required Box tradesSource, @required FiatConvertationService fiatConvertationService, - int initialMigrationVersion = 3}) async { + int initialMigrationVersion = 4}) async { await defaultSettingsMigration( version: initialMigrationVersion, sharedPreferences: sharedPreferences, diff --git a/lib/reactions/bootstrap.dart b/lib/reactions/bootstrap.dart index b190697c5..76990eb55 100644 --- a/lib/reactions/bootstrap.dart +++ b/lib/reactions/bootstrap.dart @@ -50,7 +50,8 @@ ReactionDisposer _onCurrentWalletChangeReaction; ReactionDisposer _onWalletSyncStatusChangeReaction; ReactionDisposer _onCurrentFiatCurrencyChangeDisposer; -Future bootstrap({FiatConvertationService fiatConvertationService}) async { +Future bootstrap( + {FiatConvertationService fiatConvertationService}) async { final authenticationStore = getIt.get(); final settingsStore = getIt.get(); final fiatConvertationStore = getIt.get(); @@ -72,12 +73,10 @@ Future bootstrap({FiatConvertationService fiatConvertationService}) async _onCurrentWalletChangeReaction ??= reaction((_) => getIt.get().wallet, (WalletBase wallet) async { - print('Wallet name ${wallet.name}'); - _onWalletSyncStatusChangeReaction?.reaction?.dispose(); - _onWalletSyncStatusChangeReaction = when( + _onWalletSyncStatusChangeReaction = reaction( (_) => wallet.syncStatus is ConnectedSyncStatus, - () async => await wallet.startSync()); + (_) async => await wallet.startSync()); await getIt .get() @@ -87,30 +86,24 @@ Future bootstrap({FiatConvertationService fiatConvertationService}) async .get() .setInt('current_wallet_type', serializeToInt(wallet.type)); - await wallet.connectToNode(node: null); - + final node = settingsStore.getCurrentNode(wallet.type); final cryptoCurrency = wallet.currency; final fiatCurrency = settingsStore.fiatCurrency; + await wallet.connectToNode(node: node); + final price = await fiatConvertationService.getPrice( - crypto: cryptoCurrency, - fiat: fiatCurrency - ); + crypto: cryptoCurrency, fiat: fiatCurrency); fiatConvertationStore.setPrice(price); }); - // - - _onCurrentFiatCurrencyChangeDisposer ??= - reaction((_) => settingsStore.fiatCurrency, - (FiatCurrency fiatCurrency) async { + _onCurrentFiatCurrencyChangeDisposer ??= reaction( + (_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async { final cryptoCurrency = getIt.get().wallet.currency; final price = await fiatConvertationService.getPrice( - crypto: cryptoCurrency, - fiat: fiatCurrency - ); + crypto: cryptoCurrency, fiat: fiatCurrency); fiatConvertationStore.setPrice(price); }); diff --git a/lib/src/domain/common/default_settings_migration.dart b/lib/src/domain/common/default_settings_migration.dart index d89223a4d..4ad41dff2 100644 --- a/lib/src/domain/common/default_settings_migration.dart +++ b/lib/src/domain/common/default_settings_migration.dart @@ -29,15 +29,19 @@ Future defaultSettingsMigration( switch (version) { case 1: await sharedPreferences.setString( - SettingsStoreBase.currentFiatCurrencyKey, FiatCurrency.usd.toString()); + SettingsStoreBase.currentFiatCurrencyKey, + FiatCurrency.usd.toString()); await sharedPreferences.setInt( - SettingsStoreBase.currentTransactionPriorityKey, TransactionPriority.standart.raw); + SettingsStoreBase.currentTransactionPriorityKey, + TransactionPriority.standart.raw); await sharedPreferences.setInt( SettingsStoreBase.currentBalanceDisplayModeKey, BalanceDisplayMode.availableBalance.raw); await sharedPreferences.setBool('save_recipient_address', true); await resetToDefault(nodes); - await changeCurrentNodeToDefault( + await changeMoneroCurrentNodeToDefault( + sharedPreferences: sharedPreferences, nodes: nodes); + await changeBitcoinCurrentElectrumServerToDefault( sharedPreferences: sharedPreferences, nodes: nodes); break; @@ -50,6 +54,11 @@ Future defaultSettingsMigration( case 3: await updateNodeTypes(nodes: nodes); await addBitcoinElectrumServerList(nodes: nodes); + + break; + case 4: + await changeBitcoinCurrentElectrumServerToDefault( + sharedPreferences: sharedPreferences, nodes: nodes); break; default: break; @@ -69,10 +78,11 @@ Future defaultSettingsMigration( Future replaceNodesMigration({@required Box nodes}) async { final replaceNodes = { 'eu-node.cakewallet.io:18081': - Node(uri: 'xmr-node-eu.cakewallet.com:18081'), - 'node.cakewallet.io:18081': - Node(uri: 'xmr-node-usa-east.cakewallet.com:18081'), - 'node.xmr.ru:13666': Node(uri: 'node.monero.net:18081') + Node(uri: 'xmr-node-eu.cakewallet.com:18081', type: WalletType.monero), + 'node.cakewallet.io:18081': Node( + uri: 'xmr-node-usa-east.cakewallet.com:18081', type: WalletType.monero), + 'node.xmr.ru:13666': + Node(uri: 'node.monero.net:18081', type: WalletType.monero) }; nodes.values.forEach((Node node) async { @@ -87,11 +97,27 @@ Future replaceNodesMigration({@required Box nodes}) async { }); } -Future changeCurrentNodeToDefault( +Future changeMoneroCurrentNodeToDefault( {@required SharedPreferences sharedPreferences, @required Box nodes}) async { + final node = getMoneroDefaultNode(nodes: nodes); + final nodeId = node?.key as int ?? 0; // 0 - England + + await sharedPreferences.setInt('current_node_id', nodeId); +} + +Node getBitcoinDefaultElectrumServer({@required Box nodes}) { + final uri = 'bitcoin.electrumx.multicoin.co:50002'; + + return nodes.values + .firstWhere((Node node) => node.uri == uri, orElse: () => null) ?? + nodes.values.firstWhere((node) => node.type == WalletType.bitcoin, + orElse: () => null); +} + +Node getMoneroDefaultNode({@required Box nodes}) { final timeZone = DateTime.now().timeZoneOffset.inHours; - String nodeUri = ''; + var nodeUri = ''; if (timeZone >= 1) { // Eurasia @@ -101,11 +127,18 @@ Future changeCurrentNodeToDefault( nodeUri = 'xmr-node-usa-east.cakewallet.com:18081'; } - final node = nodes.values.firstWhere((Node node) => node.uri == nodeUri) ?? + return nodes.values + .firstWhere((Node node) => node.uri == nodeUri, orElse: () => null) ?? nodes.values.first; - final nodeId = node != null ? node.key as int : 0; // 0 - England +} - await sharedPreferences.setInt('current_node_id', nodeId); +Future changeBitcoinCurrentElectrumServerToDefault( + {@required SharedPreferences sharedPreferences, + @required Box nodes}) async { + final server = getBitcoinDefaultElectrumServer(nodes: nodes); + final serverId = server?.key as int ?? 0; + + await sharedPreferences.setInt('current_node_id_btc', serverId); } Future replaceDefaultNode( @@ -126,7 +159,7 @@ Future replaceDefaultNode( return; } - await changeCurrentNodeToDefault( + await changeMoneroCurrentNodeToDefault( sharedPreferences: sharedPreferences, nodes: nodes); } diff --git a/lib/src/domain/common/node.dart b/lib/src/domain/common/node.dart index f5b91547d..b47f002e9 100644 --- a/lib/src/domain/common/node.dart +++ b/lib/src/domain/common/node.dart @@ -9,12 +9,16 @@ part 'node.g.dart'; @HiveType(typeId: 1) class Node extends HiveObject { - Node({@required this.uri, @required WalletType type, this.login, this.password}) { + Node( + {@required this.uri, + @required WalletType type, + this.login, + this.password}) { this.type = type; } Node.fromMap(Map map) - : uri = (map['uri'] ?? '') as String, + : uri = map['uri'] as String ?? '', login = map['login'] as String, password = map['password'] as String, typeRaw = map['typeRaw'] as int; diff --git a/lib/src/domain/common/node_list.dart b/lib/src/domain/common/node_list.dart index ad5db8a5d..67bfe6eac 100644 --- a/lib/src/domain/common/node_list.dart +++ b/lib/src/domain/common/node_list.dart @@ -10,7 +10,10 @@ Future> loadDefaultNodes() async { return nodes.map((dynamic raw) { if (raw is Map) { - return Node.fromMap(raw); + final node = Node.fromMap(raw); + node?.type = WalletType.monero; + + return node; } return null; @@ -38,13 +41,7 @@ Future resetToDefault(Box nodeSource) async { final moneroNodes = await loadDefaultNodes(); final bitcoinElectrumServerList = await loadElectrumServerList(); final nodes = moneroNodes + bitcoinElectrumServerList; - final entities = {}; await nodeSource.clear(); - - for (var i = 0; i < nodes.length; i++) { - entities[i] = nodes[i]; - } - - await nodeSource.putAll(entities); + await nodeSource.addAll(nodes); } diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index 1d5eab711..7a81d66cc 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -50,10 +50,7 @@ class DashboardPage extends BasePage { padding: EdgeInsets.all(0), onPressed: () async { await showDialog( - builder: (_) => MenuWidget( - name: walletViewModel.name, - subname: walletViewModel.subname, - type: walletViewModel.type), + builder: (_) => MenuWidget(walletViewModel), context: context); }, child: menuButton diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart index 2190a8d34..2de702bb9 100644 --- a/lib/src/screens/dashboard/wallet_menu.dart +++ b/lib/src/screens/dashboard/wallet_menu.dart @@ -6,8 +6,10 @@ import 'package:cake_wallet/src/stores/wallet/wallet_store.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +// FIXME: terrible design + class WalletMenu { - WalletMenu(this.context); + WalletMenu(this.context, this.reconnect); final List items = [ S.current.reconnect, @@ -30,6 +32,7 @@ class WalletMenu { ]; final BuildContext context; + final Future Function() reconnect; void action(int index) { switch (index) { @@ -70,8 +73,6 @@ class WalletMenu { } Future _presentReconnectAlert(BuildContext context) async { - final walletStore = Provider.of(context); - await showDialog( context: context, builder: (BuildContext context) { @@ -80,12 +81,11 @@ class WalletMenu { alertContent: S.of(context).reconnect_alert_text, leftButtonText: S.of(context).ok, rightButtonText: S.of(context).cancel, - actionLeftButton: () { - walletStore.reconnect(); + actionLeftButton: () async { + await reconnect?.call(); Navigator.of(context).pop(); }, - actionRightButton: () => Navigator.of(context).pop() - ); + actionRightButton: () => Navigator.of(context).pop()); }); } } diff --git a/lib/src/screens/dashboard/widgets/menu_widget.dart b/lib/src/screens/dashboard/widgets/menu_widget.dart index fdd9a704c..fab768773 100644 --- a/lib/src/screens/dashboard/widgets/menu_widget.dart +++ b/lib/src/screens/dashboard/widgets/menu_widget.dart @@ -1,14 +1,15 @@ import 'dart:ui'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/src/domain/common/wallet_type.dart'; import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart'; -class MenuWidget extends StatefulWidget { - MenuWidget({this.type, this.name, this.subname}); +// FIXME: terrible design. - final WalletType type; - final String name; - final String subname; +class MenuWidget extends StatefulWidget { + MenuWidget(this.dashboardViewModel); + + final DashboardViewModel dashboardViewModel; @override MenuWidgetState createState() => MenuWidgetState(); @@ -65,8 +66,8 @@ class MenuWidgetState extends State { @override Widget build(BuildContext context) { - final walletMenu = WalletMenu(context); -// final walletStore = Provider.of(context); + final walletMenu = + WalletMenu(context, () async => widget.dashboardViewModel.reconnect()); final itemCount = walletMenu.items.length; return Row( @@ -118,19 +119,20 @@ class MenuWidgetState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - _iconFor(type: widget.type), + _iconFor(type: widget.dashboardViewModel.type), SizedBox(width: 16), Expanded( child: Container( height: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: widget.subname != null - ? MainAxisAlignment.spaceBetween - : MainAxisAlignment.center, + mainAxisAlignment: + widget.dashboardViewModel.subname != null + ? MainAxisAlignment.spaceBetween + : MainAxisAlignment.center, children: [ Text( - widget.name, + widget.dashboardViewModel.name, style: TextStyle( color: Theme.of(context) .primaryTextTheme @@ -141,9 +143,9 @@ class MenuWidgetState extends State { fontSize: 20, fontWeight: FontWeight.bold), ), - if (widget.subname != null) + if (widget.dashboardViewModel.subname != null) Text( - widget.subname, + widget.dashboardViewModel.subname, style: TextStyle( color: Theme.of(context) .primaryTextTheme diff --git a/lib/src/screens/nodes/nodes_list_page.dart b/lib/src/screens/nodes/nodes_list_page.dart index 2777e25b7..2182c38e7 100644 --- a/lib/src/screens/nodes/nodes_list_page.dart +++ b/lib/src/screens/nodes/nodes_list_page.dart @@ -74,15 +74,41 @@ class NodeListPage extends BasePage { } final node = nodeListViewModel.nodes[index]; - final isSelected = index == 1; // FIXME: hardcoded value. final nodeListRow = NodeListRow( - title: node.uri, - isSelected: isSelected, - isAlive: node.requestNode(), - onTap: (_) {}); + title: node.value.uri, + isSelected: node.isSelected, + isAlive: node.value.requestNode(), + onTap: (_) async { + if (node.isSelected) { + return; + } + + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: Text( + S.of(context).change_current_node(node.value.uri), + textAlign: TextAlign.center, + ), + actions: [ + FlatButton( + onPressed: () => Navigator.pop(context), + child: Text(S.of(context).cancel)), + FlatButton( + onPressed: () async { + Navigator.of(context).pop(); + await nodeListViewModel + .setAsCurrent(node.value); + }, + child: Text(S.of(context).change)), + ], + ); + }); + }); final dismissibleRow = Dismissible( - key: Key('${node.key}'), + key: Key('${node.value.key}'), confirmDismiss: (direction) async { return await showDialog( context: context, @@ -99,7 +125,7 @@ class NodeListPage extends BasePage { }); }, onDismissed: (direction) async => - nodeListViewModel.delete(node), + nodeListViewModel.delete(node.value), direction: DismissDirection.endToStart, background: Container( padding: EdgeInsets.only(right: 10.0), @@ -120,7 +146,7 @@ class NodeListPage extends BasePage { )), child: nodeListRow); - return isSelected ? nodeListRow : dismissibleRow; + return node.isSelected ? nodeListRow : dismissibleRow; }, itemCounter: (int sectionIndex) { if (sectionIndex == 0) { diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 6b8e047cf..a500b2d20 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -63,14 +63,6 @@ class WalletListBodyState extends State { itemBuilder: (__, index) { final wallet = widget.walletListViewModel.wallets[index]; final screenWidth = MediaQuery.of(context).size.width; -// String shortAddress = ''; - -// if (wallet.isCurrent) { -// shortAddress = wallet.address; -// shortAddress = shortAddress.replaceRange( -// 4, shortAddress.length - 4, '...'); -// } - final walletMenu = WalletMenu(context, widget.walletListViewModel); final items = walletMenu.generateItemsForWalletMenu(wallet.isCurrent); diff --git a/lib/src/screens/wallet_list/wallet_menu.dart b/lib/src/screens/wallet_list/wallet_menu.dart index b6654d0e8..a52eb8f8e 100644 --- a/lib/src/screens/wallet_list/wallet_menu.dart +++ b/lib/src/screens/wallet_list/wallet_menu.dart @@ -109,7 +109,7 @@ class WalletMenu { try { auth.changeProcessText( S.of(context).wallet_list_removing_wallet(wallet.name)); -// await _walletListStore.remove(wallet); + await walletListViewModel.remove(wallet); auth.close(); } catch (e) { auth.changeProcessText(S diff --git a/lib/src/stores/settings/settings_store.dart b/lib/src/stores/settings/settings_store.dart index b3fe22e5a..21b56a967 100644 --- a/lib/src/stores/settings/settings_store.dart +++ b/lib/src/stores/settings/settings_store.dart @@ -42,7 +42,7 @@ abstract class SettingsStoreBase with Store { _sharedPreferences = sharedPreferences; _nodes = nodes; allowBiometricalAuthentication = initialAllowBiometricalAuthentication; - isDarkTheme = initialDarkTheme; + isDarkTheme = true; defaultPinLength = initialPinLength; languageCode = initialLanguageCode; currentLocale = initialCurrentLocale; @@ -143,7 +143,7 @@ abstract class SettingsStoreBase with Store { bool allowBiometricalAuthentication; @observable - bool isDarkTheme; + bool isDarkTheme = true; @observable int defaultPinLength; @@ -285,7 +285,7 @@ abstract class SettingsStoreBase with Store { } Future setCurrentNodeToDefault() async { - await changeCurrentNodeToDefault(sharedPreferences: _sharedPreferences, nodes: _nodes); +// await changeCurrentNodeToDefault(sharedPreferences: _sharedPreferences, nodes: _nodes); await loadSettings(); } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 7db75d309..0251fbce8 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/src/domain/common/wallet_type.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; @@ -29,8 +30,9 @@ abstract class SettingsStoreBase with Store { @required int initialPinLength, @required String initialLanguageCode, @required String initialCurrentLocale, - @required this.node, +// @required this.node, @required this.appVersion, + @required Map nodes, this.actionlistDisplayMode}) { fiatCurrency = initialFiatCurrency; transactionPriority = initialTransactionPriority; @@ -44,9 +46,11 @@ abstract class SettingsStoreBase with Store { itemHeaders = {}; _sharedPreferences = sharedPreferences; _nodeSource = nodeSource; + _nodes = nodes; } static const currentNodeIdKey = 'current_node_id'; + static const currentBitcoinElectrumSererIdKey = 'current_node_id_btc'; static const currentFiatCurrencyKey = 'current_fiat_currency'; static const currentTransactionPriorityKey = 'current_fee_priority'; static const currentBalanceDisplayModeKey = 'current_balance_display_mode'; @@ -58,8 +62,8 @@ abstract class SettingsStoreBase with Store { static const currentPinLength = 'current_pin_length'; static const currentLanguageCode = 'language_code'; - @observable - Node node; +// @observable +// Node node; @observable FiatCurrency fiatCurrency; @@ -97,6 +101,26 @@ abstract class SettingsStoreBase with Store { SharedPreferences _sharedPreferences; Box _nodeSource; + Map _nodes; + + Node getCurrentNode(WalletType walletType) => _nodes[walletType]; + + Future setCurrentNode(Node node, WalletType walletType) async { + switch (walletType) { + case WalletType.bitcoin: + await _sharedPreferences.setInt( + currentBitcoinElectrumSererIdKey, node.key as int); + break; + case WalletType.monero: + await _sharedPreferences.setInt(currentNodeIdKey, node.key as int); + break; + default: + break; + } + + _nodes[walletType] = node; + } + static Future load( {@required Box nodeSource, FiatCurrency initialFiatCurrency = FiatCurrency.usd, @@ -126,12 +150,18 @@ abstract class SettingsStoreBase with Store { await Language.localeDetection(); final initialCurrentLocale = await Devicelocale.currentLocale; final nodeId = sharedPreferences.getInt(currentNodeIdKey); - final node = nodeSource.get(nodeId); + final bitcoinElectrumServerId = + sharedPreferences.getInt(currentBitcoinElectrumSererIdKey); + final moneroNode = nodeSource.get(nodeId); + final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId); final packageInfo = await PackageInfo.fromPlatform(); return SettingsStore( sharedPreferences: sharedPreferences, - node: node, + nodes: { + WalletType.monero: moneroNode, + WalletType.bitcoin: bitcoinElectrumServer + }, nodeSource: nodeSource, appVersion: packageInfo.version, initialFiatCurrency: currentFiatCurrency, diff --git a/lib/utils/mobx.dart b/lib/utils/mobx.dart new file mode 100644 index 000000000..4dd9b4100 --- /dev/null +++ b/lib/utils/mobx.dart @@ -0,0 +1,45 @@ +import 'package:mobx/mobx.dart'; + +Dispose connectDifferent(ObservableList source, ObservableList dest, + Y Function(T) transform, {bool Function(T) filter}) { + return source.observe((change) { + switch (change.type) { + case OperationType.add: + final _values = change.added; + Iterable values; + + if (filter != null) { + values = _values.where(filter); + } + + dest.addAll(values.map((e) => transform(e))); + break; + case OperationType.remove: + print(change.index); + print(change.removed); + change.removed.forEach((element) { dest.remove(element); }); + +// dest.removeAt(change.index); + break; + case OperationType.update: +// change.index + break; + } + }); +} + +Dispose connect(ObservableList source, ObservableList dest) { + return source.observe((change) { + switch (change.type) { + case OperationType.add: + dest.addAll(change.added); + break; + case OperationType.remove: + dest.removeAt(change.index); + break; + case OperationType.update: +// change.index + break; + } + }); +} diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 7b79a772c..acb0d3d0a 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -129,6 +129,11 @@ abstract class DashboardViewModelBase with Store { ReactionDisposer _reaction; + Future reconnect() async { + final node = appStore.settingsStore.getCurrentNode(wallet.type); + await wallet.connectToNode(node: node); + } + void _onWalletChange(WalletBase wallet) { name = wallet.name; transactions.clear(); diff --git a/lib/view_model/node_list/node_list_view_model.dart b/lib/view_model/node_list/node_list_view_model.dart index 6821ae574..73f2b934e 100644 --- a/lib/view_model/node_list/node_list_view_model.dart +++ b/lib/view_model/node_list/node_list_view_model.dart @@ -1,26 +1,92 @@ +import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/src/domain/common/node.dart'; import 'package:cake_wallet/src/domain/common/node_list.dart'; import 'package:cake_wallet/store/node_list_store.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/src/domain/common/default_settings_migration.dart'; +import 'package:cake_wallet/src/domain/common/wallet_type.dart'; +import 'package:cake_wallet/utils/mobx.dart'; part 'node_list_view_model.g.dart'; class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel; -abstract class NodeListViewModelBase with Store { - NodeListViewModelBase(this._nodeListStore, this._nodeSource, this._wallet); +class ItemCell { + ItemCell(this.value, {@required this.isSelected}); - @computed - ObservableList get nodes => ObservableList.of( - _nodeListStore.nodes.where((node) => node.type == _wallet.type)); + final Item value; + final bool isSelected; +} + +abstract class NodeListViewModelBase with Store { + NodeListViewModelBase( + this._nodeListStore, this._nodeSource, this._wallet, this._settingsStore) + : nodes = ObservableList>() { + final currentNode = _settingsStore.getCurrentNode(_wallet.type); + final values = _nodeListStore.nodes; + nodes.clear(); + nodes.addAll(values.where((Node node) => node.type == _wallet.type).map( + (Node val) => + ItemCell(val, isSelected: val.key == currentNode.key))); + connectDifferent( + _nodeListStore.nodes, + nodes, + (Node val) => + ItemCell(val, isSelected: val.key == currentNode.key), + filter: (Node val) { + return val.type == _wallet.type; + }); + } + + ObservableList> nodes; final WalletBase _wallet; final Box _nodeSource; final NodeListStore _nodeListStore; + final SettingsStore _settingsStore; - Future reset() async => await resetToDefault(_nodeSource); + Future reset() async { + await resetToDefault(_nodeSource); - Future delete(Node node) async => node.delete(); + Node node; + + switch (_wallet.type) { + case WalletType.bitcoin: + node = getBitcoinDefaultElectrumServer(nodes: _nodeSource); + break; + case WalletType.monero: + node = getMoneroDefaultNode( + nodes: _nodeSource, + ); + break; + default: + break; + } + + await _wallet.connectToNode(node: node); + } + + Future delete(Node node) async => _nodeSource.delete(node.key); + + Future setAsCurrent(Node node) async { + await _wallet.connectToNode(node: node); + await _settingsStore.setCurrentNode(node, _wallet.type); + _updateCurrentNode(); + } + + void _updateCurrentNode() { + final currentNode = _settingsStore.getCurrentNode(_wallet.type); + + for (var i = 0; i < nodes.length; i++) { + final item = nodes[i]; + final isSelected = item.value.key == currentNode.key; + + if (item.isSelected != isSelected) { + nodes[i] = ItemCell(item.value, isSelected: isSelected); + } + } + } } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 0a128dc0a..75926a0b2 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -27,6 +27,7 @@ abstract class SendViewModelBase with Store { this._wallet, this._settingsStore, this._fiatConversationStore) : state = InitialSendViewModelState(), _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12, + // FIXME: need to be based on wallet type. all = false; @observable @@ -79,7 +80,7 @@ abstract class SendViewModelBase with Store { final WalletBase _wallet; final SettingsStore _settingsStore; final FiatConvertationStore _fiatConversationStore; - NumberFormat _cryptoNumberFormat; + final NumberFormat _cryptoNumberFormat; @action void setAll() => all = true; @@ -129,7 +130,8 @@ abstract class SendViewModelBase with Store { void _updateFiatAmount() { try { final fiat = calculateFiatAmount( - price: _fiatConversationStore.price, cryptoAmount: cryptoAmount); + price: _fiatConversationStore.price, + cryptoAmount: cryptoAmount.replaceAll(',', '.')); if (fiatAmount != fiat) { fiatAmount = fiat; } @@ -141,7 +143,8 @@ abstract class SendViewModelBase with Store { @action void _updateCryptoAmount() { try { - final crypto = double.parse(fiatAmount) / _fiatConversationStore.price; + final crypto = double.parse(fiatAmount.replaceAll(',', '.')) / + _fiatConversationStore.price; final cryptoAmountTmp = _cryptoNumberFormat.format(crypto); if (cryptoAmount != cryptoAmountTmp) { diff --git a/lib/view_model/settings/settings_view_model.dart b/lib/view_model/settings/settings_view_model.dart index dbca8ef66..4eb68286f 100644 --- a/lib/view_model/settings/settings_view_model.dart +++ b/lib/view_model/settings/settings_view_model.dart @@ -23,7 +23,8 @@ class SettingsViewModel = SettingsViewModelBase with _$SettingsViewModel; abstract class SettingsViewModelBase with Store { SettingsViewModelBase(this._settingsStore, WalletBase wallet) - : itemHeaders = {} { + : itemHeaders = {}, + _walletType = wallet.type { sections = [ [ PickerListItem( @@ -117,7 +118,7 @@ abstract class SettingsViewModelBase with Store { } @computed - Node get node => _settingsStore.node; + Node get node => _settingsStore.getCurrentNode(_walletType); @computed FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency; @@ -150,13 +151,10 @@ abstract class SettingsViewModelBase with Store { set allowBiometricalAuthentication(bool value) => _settingsStore.allowBiometricalAuthentication = value; -// @observable - -// @observable - final Map itemHeaders; - List> sections; final SettingsStore _settingsStore; + final WalletType _walletType; + List> sections; @action void toggleTransactionsDisplay() => diff --git a/lib/view_model/wallet_list/wallet_list_item.dart b/lib/view_model/wallet_list/wallet_list_item.dart index 669a2be79..5e2210c7a 100644 --- a/lib/view_model/wallet_list/wallet_list_item.dart +++ b/lib/view_model/wallet_list/wallet_list_item.dart @@ -3,9 +3,10 @@ import 'package:cake_wallet/src/domain/common/wallet_type.dart'; class WalletListItem { const WalletListItem( - {@required this.name, @required this.type, this.isCurrent = false}); + {@required this.name, @required this.type, @required this.key, this.isCurrent = false}); final String name; final WalletType type; final bool isCurrent; + final dynamic key; } diff --git a/lib/view_model/wallet_list/wallet_list_view_model.dart b/lib/view_model/wallet_list/wallet_list_view_model.dart index e84959e91..15683d1f1 100644 --- a/lib/view_model/wallet_list/wallet_list_view_model.dart +++ b/lib/view_model/wallet_list/wallet_list_view_model.dart @@ -17,11 +17,7 @@ abstract class WalletListViewModelBase with Store { WalletListViewModelBase( this._walletInfoSource, this._appStore, this._keyService) { wallets = ObservableList(); - wallets.addAll(_walletInfoSource.values.map((info) => WalletListItem( - name: info.name, - type: info.type, - isCurrent: info.name == _appStore.wallet.name && - info.type == _appStore.wallet.type))); + _updateList(); } @observable @@ -40,7 +36,12 @@ abstract class WalletListViewModelBase with Store { } @action - Future remove(WalletListItem wallet) async {} + Future remove(WalletListItem wallet) async { + final walletService = _getWalletService(wallet.type); + await walletService.remove(wallet.name); + await _walletInfoSource.delete(wallet.key); + _updateList(); + } WalletService _getWalletService(WalletType type) { switch (type) { @@ -52,4 +53,14 @@ abstract class WalletListViewModelBase with Store { return null; } } + + void _updateList() { + wallets.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))); + } }