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
}