TMP 0

M committed Aug 27, 2020 at 19:54 UTC 6aaac93fa8501af3faff14450374bb3a4cc99930
28 files changed +362 -141
assets/electrum_server_list.yml
+3 -1
@@ -1,2 +1,4 @@
1 -
2 - uri: electrum2.hodlister.co:50002
\ No newline at end of file
2 + uri: electrum2.hodlister.co:50002
3 +-
4 + uri: bitcoin.electrumx.multicoin.co:50002
\ No newline at end of file
ios/Podfile.lock
+2 -2
@@ -61,7 +61,7 @@ DEPENDENCIES:
61 - cw_monero (from `.symlinks/plugins/cw_monero/ios`)
62 - devicelocale (from `.symlinks/plugins/devicelocale/ios`)
63 - esys_flutter_share (from `.symlinks/plugins/esys_flutter_share/ios`)
64 - - Flutter (from `.symlinks/flutter/ios`)
64 + - Flutter (from `.symlinks/flutter/ios-release`)
65 - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`)
66 - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
67 - local_auth (from `.symlinks/plugins/local_auth/ios`)
@@ -92,7 +92,7 @@ EXTERNAL SOURCES:
92 esys_flutter_share:
93 :path: ".symlinks/plugins/esys_flutter_share/ios"
94 Flutter:
95 - :path: ".symlinks/flutter/ios"
95 + :path: ".symlinks/flutter/ios-release"
96 flutter_plugin_android_lifecycle:
97 :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios"
98 flutter_secure_storage:
ios/Runner.xcodeproj/project.pbxproj
+3 -3
@@ -373,7 +373,7 @@
373 buildSettings = {
374 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
375 CLANG_ENABLE_MODULES = YES;
376 - CURRENT_PROJECT_VERSION = 6;
376 + CURRENT_PROJECT_VERSION = 9;
377 DEVELOPMENT_TEAM = 32J6BB6VUS;
378 ENABLE_BITCODE = NO;
379 FRAMEWORK_SEARCH_PATHS = (
@@ -509,7 +509,7 @@
509 buildSettings = {
510 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
511 CLANG_ENABLE_MODULES = YES;
512 - CURRENT_PROJECT_VERSION = 6;
512 + CURRENT_PROJECT_VERSION = 9;
513 DEVELOPMENT_TEAM = 32J6BB6VUS;
514 ENABLE_BITCODE = NO;
515 FRAMEWORK_SEARCH_PATHS = (
@@ -540,7 +540,7 @@
540 buildSettings = {
541 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
542 CLANG_ENABLE_MODULES = YES;
543 - CURRENT_PROJECT_VERSION = 6;
543 + CURRENT_PROJECT_VERSION = 9;
544 DEVELOPMENT_TEAM = 32J6BB6VUS;
545 ENABLE_BITCODE = NO;
546 FRAMEWORK_SEARCH_PATHS = (
ios/Runner/Info.plist
+2
@@ -22,6 +22,8 @@
22 <string>$(CURRENT_PROJECT_VERSION)</string>
23 <key>LSRequiresIPhoneOS</key>
24 <true/>
25 + <key>NSCameraUsageDescription</key>
26 + <string>Cake Wallet requires access to your phone’s camera.</string>
27 <key>UILaunchStoryboardName</key>
28 <string>LaunchScreen</string>
29 <key>UIMainStoryboardFile</key>
lib/bitcoin/bitcoin_wallet.dart
+3 -6
@@ -200,7 +200,8 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
200 Future<void> startSync() async {
201 try {
202 syncStatus = StartingSyncStatus();
203 - transactionHistory.updateAsync(onFinished: () => print('finished!'));
203 + transactionHistory.updateAsync(
204 + onFinished: () => print('transactionHistory update finished!'));
205 _subscribeForUpdates();
206 await _updateBalance();
207 syncStatus = SyncedSyncStatus();
@@ -215,11 +216,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
216 Future<void> connectToNode({@required Node node}) async {
217 try {
218 syncStatus = ConnectingSyncStatus();
218 - // electrum2.hodlister.co
219 - // bitcoin.electrumx.multicoin.co:50002
220 - // electrum2.taborsky.cz:5002
221 - await eclient.connect(
222 - host: 'bitcoin.electrumx.multicoin.co', port: 50002);
219 + await eclient.connectToUri(node.uri);
220 syncStatus = ConnectedSyncStatus();
221 } catch (e) {
222 print(e.toString());
lib/bitcoin/bitcoin_wallet_service.dart
+3 -4
@@ -49,10 +49,9 @@ class BitcoinWalletService extends WalletService<
49 }
50
51 @override
52 - Future<void> remove(String wallet) {
53 - // TODO: implement remove
54 - throw UnimplementedError();
55 - }
52 + Future<void> remove(String wallet) async =>
53 + File(await pathForWalletDir(name: wallet, type: WalletType.bitcoin))
54 + .delete(recursive: true);
55
56 @override
57 Future<BitcoinWallet> restoreFromKeys(
lib/bitcoin/electrum.dart
+8 -4
@@ -37,11 +37,15 @@ class ElectrumClient {
37 bool _isConnected;
38 Timer _aliveTimer;
39
40 - Future<void> connect({@required String host, @required int port}) async {
41 - if (socket != null) {
42 - await socket.close();
43 - }
40 + Future<void> connectToUri(String uri) async {
41 + final _uri = Uri.parse(uri);
42 + final host = _uri.scheme;
43 + final port = int.parse(_uri.path);
44 + await connect(host: host, port: port);
45 + }
46
47 + Future<void> connect({@required String host, @required int port}) async {
48 + await socket?.close();
49 final start = DateTime.now();
50
51 socket = await SecureSocket.connect(host, port, timeout: connectionTimeout);
lib/di.dart
+20 -6
@@ -25,6 +25,7 @@ import 'package:cake_wallet/src/screens/send/send_page.dart';
25 import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
26 import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
27 import 'package:cake_wallet/store/wallet_list_store.dart';
28 +import 'package:cake_wallet/utils/mobx.dart';
29 import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
30 import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
31 import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
@@ -74,8 +75,20 @@ NodeListStore setupNodeListStore(Box<Node> nodeSource) {
75 _nodeListStore = NodeListStore();
76 _nodeListStore.replaceValues(nodeSource.values);
77 _onNodesSourceChange = nodeSource.watch();
77 - _onNodesSourceChange
78 - .listen((_) => _nodeListStore.replaceValues(nodeSource.values));
78 + _onNodesSourceChange.listen((event) {
79 +// print(event);
80 +
81 + if (event.deleted) {
82 + _nodeListStore.nodes.removeWhere((n) {
83 + return n.key != null ? n.key == event.key : true;
84 + });
85 + }
86 +
87 + if (event.value is Node) {
88 + final val = event.value as Node;
89 + _nodeListStore.nodes.add(val);
90 + }
91 + });
92
93 return _nodeListStore;
94 }
@@ -274,10 +287,11 @@ Future setup(
287 getIt.registerFactoryParam<ContactPage, Contact, void>((Contact contact, _) =>
288 ContactPage(getIt.get<ContactViewModel>(param1: contact)));
289
277 - getIt.registerFactory(() => NodeListViewModel(
278 - getIt.get<AppStore>().nodeListStore,
279 - nodeSource,
280 - getIt.get<AppStore>().wallet));
290 + getIt.registerFactory(() {
291 + final appStore = getIt.get<AppStore>();
292 + return NodeListViewModel(appStore.nodeListStore, nodeSource,
293 + appStore.wallet, appStore.settingsStore);
294 + });
295
296 getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));
297
lib/main.dart
+2 -2
@@ -127,7 +127,7 @@ void main() async {
127 contactSource: contacts,
128 tradesSource: trades,
129 fiatConvertationService: fiatConvertationService,
130 - initialMigrationVersion: 3);
130 + initialMigrationVersion: 4);
131
132 setReactions(
133 settingsStore: settingsStore,
@@ -169,7 +169,7 @@ Future<void> initialSetup(
169 @required Box<Contact> contactSource,
170 @required Box<Trade> tradesSource,
171 @required FiatConvertationService fiatConvertationService,
172 - int initialMigrationVersion = 3}) async {
172 + int initialMigrationVersion = 4}) async {
173 await defaultSettingsMigration(
174 version: initialMigrationVersion,
175 sharedPreferences: sharedPreferences,
lib/reactions/bootstrap.dart
+11 -18
@@ -50,7 +50,8 @@ ReactionDisposer _onCurrentWalletChangeReaction;
50 ReactionDisposer _onWalletSyncStatusChangeReaction;
51 ReactionDisposer _onCurrentFiatCurrencyChangeDisposer;
52
53 -Future<void> bootstrap({FiatConvertationService fiatConvertationService}) async {
53 +Future<void> bootstrap(
54 + {FiatConvertationService fiatConvertationService}) async {
55 final authenticationStore = getIt.get<AuthenticationStore>();
56 final settingsStore = getIt.get<SettingsStore>();
57 final fiatConvertationStore = getIt.get<FiatConvertationStore>();
@@ -72,12 +73,10 @@ Future<void> bootstrap({FiatConvertationService fiatConvertationService}) async
73
74 _onCurrentWalletChangeReaction ??=
75 reaction((_) => getIt.get<AppStore>().wallet, (WalletBase wallet) async {
75 - print('Wallet name ${wallet.name}');
76 -
76 _onWalletSyncStatusChangeReaction?.reaction?.dispose();
78 - _onWalletSyncStatusChangeReaction = when(
77 + _onWalletSyncStatusChangeReaction = reaction(
78 (_) => wallet.syncStatus is ConnectedSyncStatus,
80 - () async => await wallet.startSync());
79 + (_) async => await wallet.startSync());
80
81 await getIt
82 .get<SharedPreferences>()
@@ -87,30 +86,24 @@ Future<void> bootstrap({FiatConvertationService fiatConvertationService}) async
86 .get<SharedPreferences>()
87 .setInt('current_wallet_type', serializeToInt(wallet.type));
88
90 - await wallet.connectToNode(node: null);
91 -
89 + final node = settingsStore.getCurrentNode(wallet.type);
90 final cryptoCurrency = wallet.currency;
91 final fiatCurrency = settingsStore.fiatCurrency;
92
93 + await wallet.connectToNode(node: node);
94 +
95 final price = await fiatConvertationService.getPrice(
96 - crypto: cryptoCurrency,
97 - fiat: fiatCurrency
98 - );
96 + crypto: cryptoCurrency, fiat: fiatCurrency);
97
98 fiatConvertationStore.setPrice(price);
99 });
100
103 - //
104 -
105 - _onCurrentFiatCurrencyChangeDisposer ??=
106 - reaction((_) => settingsStore.fiatCurrency,
107 - (FiatCurrency fiatCurrency) async {
101 + _onCurrentFiatCurrencyChangeDisposer ??= reaction(
102 + (_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async {
103 final cryptoCurrency = getIt.get<AppStore>().wallet.currency;
104
105 final price = await fiatConvertationService.getPrice(
111 - crypto: cryptoCurrency,
112 - fiat: fiatCurrency
113 - );
106 + crypto: cryptoCurrency, fiat: fiatCurrency);
107
108 fiatConvertationStore.setPrice(price);
109 });
lib/src/domain/common/default_settings_migration.dart
+46 -13
@@ -29,15 +29,19 @@ Future defaultSettingsMigration(
29 switch (version) {
30 case 1:
31 await sharedPreferences.setString(
32 - SettingsStoreBase.currentFiatCurrencyKey, FiatCurrency.usd.toString());
32 + SettingsStoreBase.currentFiatCurrencyKey,
33 + FiatCurrency.usd.toString());
34 await sharedPreferences.setInt(
34 - SettingsStoreBase.currentTransactionPriorityKey, TransactionPriority.standart.raw);
35 + SettingsStoreBase.currentTransactionPriorityKey,
36 + TransactionPriority.standart.raw);
37 await sharedPreferences.setInt(
38 SettingsStoreBase.currentBalanceDisplayModeKey,
39 BalanceDisplayMode.availableBalance.raw);
40 await sharedPreferences.setBool('save_recipient_address', true);
41 await resetToDefault(nodes);
40 - await changeCurrentNodeToDefault(
42 + await changeMoneroCurrentNodeToDefault(
43 + sharedPreferences: sharedPreferences, nodes: nodes);
44 + await changeBitcoinCurrentElectrumServerToDefault(
45 sharedPreferences: sharedPreferences, nodes: nodes);
46
47 break;
@@ -50,6 +54,11 @@ Future defaultSettingsMigration(
54 case 3:
55 await updateNodeTypes(nodes: nodes);
56 await addBitcoinElectrumServerList(nodes: nodes);
57 +
58 + break;
59 + case 4:
60 + await changeBitcoinCurrentElectrumServerToDefault(
61 + sharedPreferences: sharedPreferences, nodes: nodes);
62 break;
63 default:
64 break;
@@ -69,10 +78,11 @@ Future defaultSettingsMigration(
78 Future<void> replaceNodesMigration({@required Box<Node> nodes}) async {
79 final replaceNodes = <String, Node>{
80 'eu-node.cakewallet.io:18081':
72 - Node(uri: 'xmr-node-eu.cakewallet.com:18081'),
73 - 'node.cakewallet.io:18081':
74 - Node(uri: 'xmr-node-usa-east.cakewallet.com:18081'),
75 - 'node.xmr.ru:13666': Node(uri: 'node.monero.net:18081')
81 + Node(uri: 'xmr-node-eu.cakewallet.com:18081', type: WalletType.monero),
82 + 'node.cakewallet.io:18081': Node(
83 + uri: 'xmr-node-usa-east.cakewallet.com:18081', type: WalletType.monero),
84 + 'node.xmr.ru:13666':
85 + Node(uri: 'node.monero.net:18081', type: WalletType.monero)
86 };
87
88 nodes.values.forEach((Node node) async {
@@ -87,11 +97,27 @@ Future<void> replaceNodesMigration({@required Box<Node> nodes}) async {
97 });
98 }
99
90 -Future<void> changeCurrentNodeToDefault(
100 +Future<void> changeMoneroCurrentNodeToDefault(
101 {@required SharedPreferences sharedPreferences,
102 @required Box<Node> nodes}) async {
103 + final node = getMoneroDefaultNode(nodes: nodes);
104 + final nodeId = node?.key as int ?? 0; // 0 - England
105 +
106 + await sharedPreferences.setInt('current_node_id', nodeId);
107 +}
108 +
109 +Node getBitcoinDefaultElectrumServer({@required Box<Node> nodes}) {
110 + final uri = 'bitcoin.electrumx.multicoin.co:50002';
111 +
112 + return nodes.values
113 + .firstWhere((Node node) => node.uri == uri, orElse: () => null) ??
114 + nodes.values.firstWhere((node) => node.type == WalletType.bitcoin,
115 + orElse: () => null);
116 +}
117 +
118 +Node getMoneroDefaultNode({@required Box<Node> nodes}) {
119 final timeZone = DateTime.now().timeZoneOffset.inHours;
94 - String nodeUri = '';
120 + var nodeUri = '';
121
122 if (timeZone >= 1) {
123 // Eurasia
@@ -101,11 +127,18 @@ Future<void> changeCurrentNodeToDefault(
127 nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
128 }
129
104 - final node = nodes.values.firstWhere((Node node) => node.uri == nodeUri) ??
130 + return nodes.values
131 + .firstWhere((Node node) => node.uri == nodeUri, orElse: () => null) ??
132 nodes.values.first;
106 - final nodeId = node != null ? node.key as int : 0; // 0 - England
133 +}
134
108 - await sharedPreferences.setInt('current_node_id', nodeId);
135 +Future<void> changeBitcoinCurrentElectrumServerToDefault(
136 + {@required SharedPreferences sharedPreferences,
137 + @required Box<Node> nodes}) async {
138 + final server = getBitcoinDefaultElectrumServer(nodes: nodes);
139 + final serverId = server?.key as int ?? 0;
140 +
141 + await sharedPreferences.setInt('current_node_id_btc', serverId);
142 }
143
144 Future<void> replaceDefaultNode(
@@ -126,7 +159,7 @@ Future<void> replaceDefaultNode(
159 return;
160 }
161
129 - await changeCurrentNodeToDefault(
162 + await changeMoneroCurrentNodeToDefault(
163 sharedPreferences: sharedPreferences, nodes: nodes);
164 }
165
lib/src/domain/common/node.dart
+6 -2
@@ -9,12 +9,16 @@ part 'node.g.dart';
9
10 @HiveType(typeId: 1)
11 class Node extends HiveObject {
12 - Node({@required this.uri, @required WalletType type, this.login, this.password}) {
12 + Node(
13 + {@required this.uri,
14 + @required WalletType type,
15 + this.login,
16 + this.password}) {
17 this.type = type;
18 }
19
20 Node.fromMap(Map map)
17 - : uri = (map['uri'] ?? '') as String,
21 + : uri = map['uri'] as String ?? '',
22 login = map['login'] as String,
23 password = map['password'] as String,
24 typeRaw = map['typeRaw'] as int;
lib/src/domain/common/node_list.dart
+5 -8
@@ -10,7 +10,10 @@ Future<List<Node>> loadDefaultNodes() async {
10
11 return nodes.map((dynamic raw) {
12 if (raw is Map) {
13 - return Node.fromMap(raw);
13 + final node = Node.fromMap(raw);
14 + node?.type = WalletType.monero;
15 +
16 + return node;
17 }
18
19 return null;
@@ -38,13 +41,7 @@ Future resetToDefault(Box<Node> nodeSource) async {
41 final moneroNodes = await loadDefaultNodes();
42 final bitcoinElectrumServerList = await loadElectrumServerList();
43 final nodes = moneroNodes + bitcoinElectrumServerList;
41 - final entities = <int, Node>{};
44
45 await nodeSource.clear();
44 -
45 - for (var i = 0; i < nodes.length; i++) {
46 - entities[i] = nodes[i];
47 - }
48 -
49 - await nodeSource.putAll(entities);
46 + await nodeSource.addAll(nodes);
47 }
lib/src/screens/dashboard/dashboard_page.dart
+1 -4
@@ -50,10 +50,7 @@ class DashboardPage extends BasePage {
50 padding: EdgeInsets.all(0),
51 onPressed: () async {
52 await showDialog<void>(
53 - builder: (_) => MenuWidget(
54 - name: walletViewModel.name,
55 - subname: walletViewModel.subname,
56 - type: walletViewModel.type),
53 + builder: (_) => MenuWidget(walletViewModel),
54 context: context);
55 },
56 child: menuButton
lib/src/screens/dashboard/wallet_menu.dart
+7 -7
@@ -6,8 +6,10 @@ import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
6 import 'package:cake_wallet/src/screens/auth/auth_page.dart';
7 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
8
9 +// FIXME: terrible design
10 +
11 class WalletMenu {
10 - WalletMenu(this.context);
12 + WalletMenu(this.context, this.reconnect);
13
14 final List<String> items = [
15 S.current.reconnect,
@@ -30,6 +32,7 @@ class WalletMenu {
32 ];
33
34 final BuildContext context;
35 + final Future<void> Function() reconnect;
36
37 void action(int index) {
38 switch (index) {
@@ -70,8 +73,6 @@ class WalletMenu {
73 }
74
75 Future<void> _presentReconnectAlert(BuildContext context) async {
73 - final walletStore = Provider.of<WalletStore>(context);
74 -
76 await showDialog<void>(
77 context: context,
78 builder: (BuildContext context) {
@@ -80,12 +81,11 @@ class WalletMenu {
81 alertContent: S.of(context).reconnect_alert_text,
82 leftButtonText: S.of(context).ok,
83 rightButtonText: S.of(context).cancel,
83 - actionLeftButton: () {
84 - walletStore.reconnect();
84 + actionLeftButton: () async {
85 + await reconnect?.call();
86 Navigator.of(context).pop();
87 },
87 - actionRightButton: () => Navigator.of(context).pop()
88 - );
88 + actionRightButton: () => Navigator.of(context).pop());
89 });
90 }
91 }
lib/src/screens/dashboard/widgets/menu_widget.dart
+15 -13
@@ -1,14 +1,15 @@
1 import 'dart:ui';
2 +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
3 import 'package:flutter/material.dart';
4 import 'package:cake_wallet/src/domain/common/wallet_type.dart';
5 import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
6
7 +// FIXME: terrible design.
8 +
9 class MenuWidget extends StatefulWidget {
7 - MenuWidget({this.type, this.name, this.subname});
10 + MenuWidget(this.dashboardViewModel);
11
9 - final WalletType type;
10 - final String name;
11 - final String subname;
12 + final DashboardViewModel dashboardViewModel;
13
14 @override
15 MenuWidgetState createState() => MenuWidgetState();
@@ -65,8 +66,8 @@ class MenuWidgetState extends State<MenuWidget> {
66
67 @override
68 Widget build(BuildContext context) {
68 - final walletMenu = WalletMenu(context);
69 -// final walletStore = Provider.of<WalletStore>(context);
69 + final walletMenu =
70 + WalletMenu(context, () async => widget.dashboardViewModel.reconnect());
71 final itemCount = walletMenu.items.length;
72
73 return Row(
@@ -118,19 +119,20 @@ class MenuWidgetState extends State<MenuWidget> {
119 child: Row(
120 mainAxisAlignment: MainAxisAlignment.start,
121 children: <Widget>[
121 - _iconFor(type: widget.type),
122 + _iconFor(type: widget.dashboardViewModel.type),
123 SizedBox(width: 16),
124 Expanded(
125 child: Container(
126 height: 40,
127 child: Column(
128 crossAxisAlignment: CrossAxisAlignment.start,
128 - mainAxisAlignment: widget.subname != null
129 - ? MainAxisAlignment.spaceBetween
130 - : MainAxisAlignment.center,
129 + mainAxisAlignment:
130 + widget.dashboardViewModel.subname != null
131 + ? MainAxisAlignment.spaceBetween
132 + : MainAxisAlignment.center,
133 children: <Widget>[
134 Text(
133 - widget.name,
135 + widget.dashboardViewModel.name,
136 style: TextStyle(
137 color: Theme.of(context)
138 .primaryTextTheme
@@ -141,9 +143,9 @@ class MenuWidgetState extends State<MenuWidget> {
143 fontSize: 20,
144 fontWeight: FontWeight.bold),
145 ),
144 - if (widget.subname != null)
146 + if (widget.dashboardViewModel.subname != null)
147 Text(
146 - widget.subname,
148 + widget.dashboardViewModel.subname,
149 style: TextStyle(
150 color: Theme.of(context)
151 .primaryTextTheme
lib/src/screens/nodes/nodes_list_page.dart
+34 -8
@@ -74,15 +74,41 @@ class NodeListPage extends BasePage {
74 }
75
76 final node = nodeListViewModel.nodes[index];
77 - final isSelected = index == 1; // FIXME: hardcoded value.
77 final nodeListRow = NodeListRow(
79 - title: node.uri,
80 - isSelected: isSelected,
81 - isAlive: node.requestNode(),
82 - onTap: (_) {});
78 + title: node.value.uri,
79 + isSelected: node.isSelected,
80 + isAlive: node.value.requestNode(),
81 + onTap: (_) async {
82 + if (node.isSelected) {
83 + return;
84 + }
85 +
86 + await showDialog<void>(
87 + context: context,
88 + builder: (BuildContext context) {
89 + return AlertDialog(
90 + content: Text(
91 + S.of(context).change_current_node(node.value.uri),
92 + textAlign: TextAlign.center,
93 + ),
94 + actions: <Widget>[
95 + FlatButton(
96 + onPressed: () => Navigator.pop(context),
97 + child: Text(S.of(context).cancel)),
98 + FlatButton(
99 + onPressed: () async {
100 + Navigator.of(context).pop();
101 + await nodeListViewModel
102 + .setAsCurrent(node.value);
103 + },
104 + child: Text(S.of(context).change)),
105 + ],
106 + );
107 + });
108 + });
109
110 final dismissibleRow = Dismissible(
85 - key: Key('${node.key}'),
111 + key: Key('${node.value.key}'),
112 confirmDismiss: (direction) async {
113 return await showDialog(
114 context: context,
@@ -99,7 +125,7 @@ class NodeListPage extends BasePage {
125 });
126 },
127 onDismissed: (direction) async =>
102 - nodeListViewModel.delete(node),
128 + nodeListViewModel.delete(node.value),
129 direction: DismissDirection.endToStart,
130 background: Container(
131 padding: EdgeInsets.only(right: 10.0),
@@ -120,7 +146,7 @@ class NodeListPage extends BasePage {
146 )),
147 child: nodeListRow);
148
123 - return isSelected ? nodeListRow : dismissibleRow;
149 + return node.isSelected ? nodeListRow : dismissibleRow;
150 },
151 itemCounter: (int sectionIndex) {
152 if (sectionIndex == 0) {
lib/src/screens/wallet_list/wallet_list_page.dart
-8
@@ -63,14 +63,6 @@ class WalletListBodyState extends State<WalletListBody> {
63 itemBuilder: (__, index) {
64 final wallet = widget.walletListViewModel.wallets[index];
65 final screenWidth = MediaQuery.of(context).size.width;
66 -// String shortAddress = '';
67 -
68 -// if (wallet.isCurrent) {
69 -// shortAddress = wallet.address;
70 -// shortAddress = shortAddress.replaceRange(
71 -// 4, shortAddress.length - 4, '...');
72 -// }
73 -
66 final walletMenu = WalletMenu(context, widget.walletListViewModel);
67 final items =
68 walletMenu.generateItemsForWalletMenu(wallet.isCurrent);
lib/src/screens/wallet_list/wallet_menu.dart
+1 -1
@@ -109,7 +109,7 @@ class WalletMenu {
109 try {
110 auth.changeProcessText(
111 S.of(context).wallet_list_removing_wallet(wallet.name));
112 -// await _walletListStore.remove(wallet);
112 + await walletListViewModel.remove(wallet);
113 auth.close();
114 } catch (e) {
115 auth.changeProcessText(S
lib/src/stores/settings/settings_store.dart
+3 -3
@@ -42,7 +42,7 @@ abstract class SettingsStoreBase with Store {
42 _sharedPreferences = sharedPreferences;
43 _nodes = nodes;
44 allowBiometricalAuthentication = initialAllowBiometricalAuthentication;
45 - isDarkTheme = initialDarkTheme;
45 + isDarkTheme = true;
46 defaultPinLength = initialPinLength;
47 languageCode = initialLanguageCode;
48 currentLocale = initialCurrentLocale;
@@ -143,7 +143,7 @@ abstract class SettingsStoreBase with Store {
143 bool allowBiometricalAuthentication;
144
145 @observable
146 - bool isDarkTheme;
146 + bool isDarkTheme = true;
147
148 @observable
149 int defaultPinLength;
@@ -285,7 +285,7 @@ abstract class SettingsStoreBase with Store {
285 }
286
287 Future setCurrentNodeToDefault() async {
288 - await changeCurrentNodeToDefault(sharedPreferences: _sharedPreferences, nodes: _nodes);
288 +// await changeCurrentNodeToDefault(sharedPreferences: _sharedPreferences, nodes: _nodes);
289 await loadSettings();
290 }
291
lib/store/settings_store.dart
+35 -5
@@ -1,4 +1,5 @@
1 import 'package:cake_wallet/di.dart';
2 +import 'package:cake_wallet/src/domain/common/wallet_type.dart';
3 import 'package:flutter/foundation.dart';
4 import 'package:hive/hive.dart';
5 import 'package:mobx/mobx.dart';
@@ -29,8 +30,9 @@ abstract class SettingsStoreBase with Store {
30 @required int initialPinLength,
31 @required String initialLanguageCode,
32 @required String initialCurrentLocale,
32 - @required this.node,
33 +// @required this.node,
34 @required this.appVersion,
35 + @required Map<WalletType, Node> nodes,
36 this.actionlistDisplayMode}) {
37 fiatCurrency = initialFiatCurrency;
38 transactionPriority = initialTransactionPriority;
@@ -44,9 +46,11 @@ abstract class SettingsStoreBase with Store {
46 itemHeaders = {};
47 _sharedPreferences = sharedPreferences;
48 _nodeSource = nodeSource;
49 + _nodes = nodes;
50 }
51
52 static const currentNodeIdKey = 'current_node_id';
53 + static const currentBitcoinElectrumSererIdKey = 'current_node_id_btc';
54 static const currentFiatCurrencyKey = 'current_fiat_currency';
55 static const currentTransactionPriorityKey = 'current_fee_priority';
56 static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
@@ -58,8 +62,8 @@ abstract class SettingsStoreBase with Store {
62 static const currentPinLength = 'current_pin_length';
63 static const currentLanguageCode = 'language_code';
64
61 - @observable
62 - Node node;
65 +// @observable
66 +// Node node;
67
68 @observable
69 FiatCurrency fiatCurrency;
@@ -97,6 +101,26 @@ abstract class SettingsStoreBase with Store {
101 SharedPreferences _sharedPreferences;
102 Box<Node> _nodeSource;
103
104 + Map<WalletType, Node> _nodes;
105 +
106 + Node getCurrentNode(WalletType walletType) => _nodes[walletType];
107 +
108 + Future<void> setCurrentNode(Node node, WalletType walletType) async {
109 + switch (walletType) {
110 + case WalletType.bitcoin:
111 + await _sharedPreferences.setInt(
112 + currentBitcoinElectrumSererIdKey, node.key as int);
113 + break;
114 + case WalletType.monero:
115 + await _sharedPreferences.setInt(currentNodeIdKey, node.key as int);
116 + break;
117 + default:
118 + break;
119 + }
120 +
121 + _nodes[walletType] = node;
122 + }
123 +
124 static Future<SettingsStore> load(
125 {@required Box<Node> nodeSource,
126 FiatCurrency initialFiatCurrency = FiatCurrency.usd,
@@ -126,12 +150,18 @@ abstract class SettingsStoreBase with Store {
150 await Language.localeDetection();
151 final initialCurrentLocale = await Devicelocale.currentLocale;
152 final nodeId = sharedPreferences.getInt(currentNodeIdKey);
129 - final node = nodeSource.get(nodeId);
153 + final bitcoinElectrumServerId =
154 + sharedPreferences.getInt(currentBitcoinElectrumSererIdKey);
155 + final moneroNode = nodeSource.get(nodeId);
156 + final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
157 final packageInfo = await PackageInfo.fromPlatform();
158
159 return SettingsStore(
160 sharedPreferences: sharedPreferences,
134 - node: node,
161 + nodes: {
162 + WalletType.monero: moneroNode,
163 + WalletType.bitcoin: bitcoinElectrumServer
164 + },
165 nodeSource: nodeSource,
166 appVersion: packageInfo.version,
167 initialFiatCurrency: currentFiatCurrency,
lib/utils/mobx.dart new
+45
@@ -0,0 +1,45 @@
1 +import 'package:mobx/mobx.dart';
2 +
3 +Dispose connectDifferent<T, Y>(ObservableList<T> source, ObservableList<Y> dest,
4 + Y Function(T) transform, {bool Function(T) filter}) {
5 + return source.observe((change) {
6 + switch (change.type) {
7 + case OperationType.add:
8 + final _values = change.added;
9 + Iterable<T> values;
10 +
11 + if (filter != null) {
12 + values = _values.where(filter);
13 + }
14 +
15 + dest.addAll(values.map((e) => transform(e)));
16 + break;
17 + case OperationType.remove:
18 + print(change.index);
19 + print(change.removed);
20 + change.removed.forEach((element) { dest.remove(element); });
21 +
22 +// dest.removeAt(change.index);
23 + break;
24 + case OperationType.update:
25 +// change.index
26 + break;
27 + }
28 + });
29 +}
30 +
31 +Dispose connect<T>(ObservableList<T> source, ObservableList<T> dest) {
32 + return source.observe((change) {
33 + switch (change.type) {
34 + case OperationType.add:
35 + dest.addAll(change.added);
36 + break;
37 + case OperationType.remove:
38 + dest.removeAt(change.index);
39 + break;
40 + case OperationType.update:
41 +// change.index
42 + break;
43 + }
44 + });
45 +}
lib/view_model/dashboard/dashboard_view_model.dart
+5
@@ -129,6 +129,11 @@ abstract class DashboardViewModelBase with Store {
129
130 ReactionDisposer _reaction;
131
132 + Future<void> reconnect() async {
133 + final node = appStore.settingsStore.getCurrentNode(wallet.type);
134 + await wallet.connectToNode(node: node);
135 + }
136 +
137 void _onWalletChange(WalletBase wallet) {
138 name = wallet.name;
139 transactions.clear();
lib/view_model/node_list/node_list_view_model.dart
+72 -6
@@ -1,26 +1,92 @@
1 +import 'package:flutter/foundation.dart';
2 import 'package:hive/hive.dart';
3 import 'package:mobx/mobx.dart';
4 import 'package:cake_wallet/core/wallet_base.dart';
5 import 'package:cake_wallet/src/domain/common/node.dart';
6 import 'package:cake_wallet/src/domain/common/node_list.dart';
7 import 'package:cake_wallet/store/node_list_store.dart';
8 +import 'package:cake_wallet/store/settings_store.dart';
9 +import 'package:cake_wallet/src/domain/common/default_settings_migration.dart';
10 +import 'package:cake_wallet/src/domain/common/wallet_type.dart';
11 +import 'package:cake_wallet/utils/mobx.dart';
12
13 part 'node_list_view_model.g.dart';
14
15 class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel;
16
17 +class ItemCell<Item> {
18 + ItemCell(this.value, {@required this.isSelected});
19 +
20 + final Item value;
21 + final bool isSelected;
22 +}
23 +
24 abstract class NodeListViewModelBase with Store {
13 - NodeListViewModelBase(this._nodeListStore, this._nodeSource, this._wallet);
25 + NodeListViewModelBase(
26 + this._nodeListStore, this._nodeSource, this._wallet, this._settingsStore)
27 + : nodes = ObservableList<ItemCell<Node>>() {
28 + final currentNode = _settingsStore.getCurrentNode(_wallet.type);
29 + final values = _nodeListStore.nodes;
30 + nodes.clear();
31 + nodes.addAll(values.where((Node node) => node.type == _wallet.type).map(
32 + (Node val) =>
33 + ItemCell<Node>(val, isSelected: val.key == currentNode.key)));
34 + connectDifferent(
35 + _nodeListStore.nodes,
36 + nodes,
37 + (Node val) =>
38 + ItemCell<Node>(val, isSelected: val.key == currentNode.key),
39 + filter: (Node val) {
40 + return val.type == _wallet.type;
41 + });
42 + }
43
15 - @computed
16 - ObservableList<Node> get nodes => ObservableList<Node>.of(
17 - _nodeListStore.nodes.where((node) => node.type == _wallet.type));
44 + ObservableList<ItemCell<Node>> nodes;
45
46 final WalletBase _wallet;
47 final Box<Node> _nodeSource;
48 final NodeListStore _nodeListStore;
49 + final SettingsStore _settingsStore;
50 +
51 + Future<void> reset() async {
52 + await resetToDefault(_nodeSource);
53 +
54 + Node node;
55 +
56 + switch (_wallet.type) {
57 + case WalletType.bitcoin:
58 + node = getBitcoinDefaultElectrumServer(nodes: _nodeSource);
59 + break;
60 + case WalletType.monero:
61 + node = getMoneroDefaultNode(
62 + nodes: _nodeSource,
63 + );
64 + break;
65 + default:
66 + break;
67 + }
68 +
69 + await _wallet.connectToNode(node: node);
70 + }
71 +
72 + Future<void> delete(Node node) async => _nodeSource.delete(node.key);
73 +
74 + Future<void> setAsCurrent(Node node) async {
75 + await _wallet.connectToNode(node: node);
76 + await _settingsStore.setCurrentNode(node, _wallet.type);
77 + _updateCurrentNode();
78 + }
79 +
80 + void _updateCurrentNode() {
81 + final currentNode = _settingsStore.getCurrentNode(_wallet.type);
82
23 - Future<void> reset() async => await resetToDefault(_nodeSource);
83 + for (var i = 0; i < nodes.length; i++) {
84 + final item = nodes[i];
85 + final isSelected = item.value.key == currentNode.key;
86
25 - Future<void> delete(Node node) async => node.delete();
87 + if (item.isSelected != isSelected) {
88 + nodes[i] = ItemCell<Node>(item.value, isSelected: isSelected);
89 + }
90 + }
91 + }
92 }
lib/view_model/send/send_view_model.dart
+6 -3
@@ -27,6 +27,7 @@ abstract class SendViewModelBase with Store {
27 this._wallet, this._settingsStore, this._fiatConversationStore)
28 : state = InitialSendViewModelState(),
29 _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12,
30 + // FIXME: need to be based on wallet type.
31 all = false;
32
33 @observable
@@ -79,7 +80,7 @@ abstract class SendViewModelBase with Store {
80 final WalletBase _wallet;
81 final SettingsStore _settingsStore;
82 final FiatConvertationStore _fiatConversationStore;
82 - NumberFormat _cryptoNumberFormat;
83 + final NumberFormat _cryptoNumberFormat;
84
85 @action
86 void setAll() => all = true;
@@ -129,7 +130,8 @@ abstract class SendViewModelBase with Store {
130 void _updateFiatAmount() {
131 try {
132 final fiat = calculateFiatAmount(
132 - price: _fiatConversationStore.price, cryptoAmount: cryptoAmount);
133 + price: _fiatConversationStore.price,
134 + cryptoAmount: cryptoAmount.replaceAll(',', '.'));
135 if (fiatAmount != fiat) {
136 fiatAmount = fiat;
137 }
@@ -141,7 +143,8 @@ abstract class SendViewModelBase with Store {
143 @action
144 void _updateCryptoAmount() {
145 try {
144 - final crypto = double.parse(fiatAmount) / _fiatConversationStore.price;
146 + final crypto = double.parse(fiatAmount.replaceAll(',', '.')) /
147 + _fiatConversationStore.price;
148 final cryptoAmountTmp = _cryptoNumberFormat.format(crypto);
149
150 if (cryptoAmount != cryptoAmountTmp) {
lib/view_model/settings/settings_view_model.dart
+5 -7
@@ -23,7 +23,8 @@ class SettingsViewModel = SettingsViewModelBase with _$SettingsViewModel;
23
24 abstract class SettingsViewModelBase with Store {
25 SettingsViewModelBase(this._settingsStore, WalletBase wallet)
26 - : itemHeaders = {} {
26 + : itemHeaders = {},
27 + _walletType = wallet.type {
28 sections = [
29 [
30 PickerListItem(
@@ -117,7 +118,7 @@ abstract class SettingsViewModelBase with Store {
118 }
119
120 @computed
120 - Node get node => _settingsStore.node;
121 + Node get node => _settingsStore.getCurrentNode(_walletType);
122
123 @computed
124 FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency;
@@ -150,13 +151,10 @@ abstract class SettingsViewModelBase with Store {
151 set allowBiometricalAuthentication(bool value) =>
152 _settingsStore.allowBiometricalAuthentication = value;
153
153 -// @observable
154 -
155 -// @observable
156 -
154 final Map<String, String> itemHeaders;
158 - List<List<SettingsListItem>> sections;
155 final SettingsStore _settingsStore;
156 + final WalletType _walletType;
157 + List<List<SettingsListItem>> sections;
158
159 @action
160 void toggleTransactionsDisplay() =>
lib/view_model/wallet_list/wallet_list_item.dart
+2 -1
@@ -3,9 +3,10 @@ import 'package:cake_wallet/src/domain/common/wallet_type.dart';
3
4 class WalletListItem {
5 const WalletListItem(
6 - {@required this.name, @required this.type, this.isCurrent = false});
6 + {@required this.name, @required this.type, @required this.key, this.isCurrent = false});
7
8 final String name;
9 final WalletType type;
10 final bool isCurrent;
11 + final dynamic key;
12 }
lib/view_model/wallet_list/wallet_list_view_model.dart
+17 -6
@@ -17,11 +17,7 @@ abstract class WalletListViewModelBase with Store {
17 WalletListViewModelBase(
18 this._walletInfoSource, this._appStore, this._keyService) {
19 wallets = ObservableList<WalletListItem>();
20 - wallets.addAll(_walletInfoSource.values.map((info) => WalletListItem(
21 - name: info.name,
22 - type: info.type,
23 - isCurrent: info.name == _appStore.wallet.name &&
24 - info.type == _appStore.wallet.type)));
20 + _updateList();
21 }
22
23 @observable
@@ -40,7 +36,12 @@ abstract class WalletListViewModelBase with Store {
36 }
37
38 @action
43 - Future<void> remove(WalletListItem wallet) async {}
39 + Future<void> remove(WalletListItem wallet) async {
40 + final walletService = _getWalletService(wallet.type);
41 + await walletService.remove(wallet.name);
42 + await _walletInfoSource.delete(wallet.key);
43 + _updateList();
44 + }
45
46 WalletService _getWalletService(WalletType type) {
47 switch (type) {
@@ -52,4 +53,14 @@ abstract class WalletListViewModelBase with Store {
53 return null;
54 }
55 }
56 +
57 + void _updateList() {
58 + wallets.clear();
59 + wallets.addAll(_walletInfoSource.values.map((info) => WalletListItem(
60 + name: info.name,
61 + type: info.type,
62 + key: info.key,
63 + isCurrent: info.name == _appStore.wallet.name &&
64 + info.type == _appStore.wallet.type)));
65 + }
66 }