TMP 2
M committed
Aug 29, 2020 at 13:19 UTC
24d139e540096339844de029528dd443e4f99d1e
11 files changed
+114
-31
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 = 9;
376
+ CURRENT_PROJECT_VERSION = 12;
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 = 9;
512
+ CURRENT_PROJECT_VERSION = 12;
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 = 9;
543
+ CURRENT_PROJECT_VERSION = 12;
544
DEVELOPMENT_TEAM = 32J6BB6VUS;
545
ENABLE_BITCODE = NO;
546
FRAMEWORK_SEARCH_PATHS = (
lib/bitcoin/bitcoin_transaction_info.dart
+2
-2
@@ -16,12 +16,13 @@ class BitcoinTransactionInfo extends TransactionInfo {
16
@required TransactionDirection direction,
17
@required bool isPending,
18
@required DateTime date,
19
- @required this.confirmations}) {
19
+ @required int confirmations}) {
20
this.height = height;
21
this.amount = amount;
22
this.direction = direction;
23
this.date = date;
24
this.isPending = isPending;
25
+ this.confirmations = confirmations;
26
}
27
28
factory BitcoinTransactionInfo.fromElectrumVerbose(Map<String, Object> obj,
@@ -119,7 +120,6 @@ class BitcoinTransactionInfo extends TransactionInfo {
120
}
121
122
final String id;
122
- int confirmations;
123
124
String _fiatAmount;
125
lib/bitcoin/bitcoin_wallet.dart
+9
-1
@@ -1,3 +1,4 @@
1
+import 'dart:async';
2
import 'dart:typed_data';
3
import 'dart:convert';
4
import 'package:cake_wallet/bitcoin/bitcoin_transaction_credentials.dart';
@@ -31,9 +32,12 @@ import 'package:cake_wallet/src/domain/common/node.dart';
32
import 'package:cake_wallet/core/wallet_base.dart';
33
import 'package:rxdart/rxdart.dart';
34
import 'package:hex/hex.dart';
35
+import 'package:cake_wallet/di.dart';
36
+import 'package:shared_preferences/shared_preferences.dart';
37
38
part 'bitcoin_wallet.g.dart';
39
40
+
41
class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
42
43
abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
@@ -217,6 +221,11 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
221
try {
222
syncStatus = ConnectingSyncStatus();
223
await eclient.connectToUri(node.uri);
224
+ eclient.onConnectionStatusChange = (bool isConnected) {
225
+ if (!isConnected) {
226
+ syncStatus = LostConnectionSyncStatus();
227
+ }
228
+ };
229
syncStatus = ConnectedSyncStatus();
230
} catch (e) {
231
print(e.toString());
@@ -331,7 +340,6 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
340
await _scripthashesUpdateSubject[sh]?.close();
341
_scripthashesUpdateSubject[sh] = eclient.scripthashUpdate(sh);
342
_scripthashesUpdateSubject[sh].listen((event) async {
334
- print('event $event');
343
transactionHistory.updateAsync();
344
await _updateBalance();
345
});
lib/bitcoin/electrum.dart
+57
-22
@@ -32,6 +32,7 @@ class ElectrumClient {
32
33
bool get isConnected => _isConnected;
34
Socket socket;
35
+ void Function(bool) onConnectionStatusChange;
36
int _id;
37
final Map<String, SocketTask> _tasks;
38
bool _isConnected;
@@ -45,50 +46,49 @@ class ElectrumClient {
46
}
47
48
Future<void> connect({@required String host, @required int port}) async {
48
- await socket?.close();
49
- final start = DateTime.now();
49
+ try {
50
+ await socket?.close();
51
+ } catch (_) {}
52
53
socket = await SecureSocket.connect(host, port, timeout: connectionTimeout);
52
-
53
- _isConnected = true;
54
-
54
+ _setIsConnected(true);
55
socket.listen((List<int> event) {
56
try {
57
final jsoned = json.decode(utf8.decode(event)) as Map<String, Object>;
58
-// print(jsoned);
58
+ print(jsoned);
59
final method = jsoned['method'];
60
+ final id = jsoned['id'] as String;
61
+ final params = jsoned['result'];
62
63
if (method is String) {
64
_methodHandler(method: method, request: jsoned);
65
return;
66
}
67
66
- final id = jsoned['id'] as String;
67
- final params = jsoned['result'];
68
-
68
_finish(id, params);
69
} catch (e) {
70
print(e);
71
}
73
- }, onError: (Object error) {
74
- print('ElectrumClient error: ${error.toString()}');
75
- }, onDone: () {
76
- final end = DateTime.now();
77
- final diff = end.millisecondsSinceEpoch - start.millisecondsSinceEpoch;
78
- print('On done: $diff');
79
- });
80
-
81
- print('Connected to ${socket.remoteAddress}');
72
+ },
73
+ onError: (_) => _setIsConnected(false),
74
+ onDone: () => _setIsConnected(false));
75
keepAlive();
76
}
77
78
void keepAlive() {
79
_aliveTimer?.cancel();
80
// FIXME: Unnamed constant.
88
- _aliveTimer = Timer.periodic(Duration(seconds: 30), (_) async => ping());
81
+ _aliveTimer = Timer.periodic(Duration(seconds: 2), (_) async => ping());
82
}
83
91
- Future<void> ping() => call(method: 'server.ping');
84
+ Future<void> ping() async {
85
+ try {
86
+ await callWithTimeout(method: 'server.ping');
87
+ _setIsConnected(true);
88
+ } on RequestFailedTimeoutException catch (_) {
89
+ _setIsConnected(false);
90
+ }
91
+ }
92
93
Future<List<String>> version() =>
94
call(method: 'server.version').then((dynamic result) {
@@ -205,7 +205,6 @@ class ElectrumClient {
205
{@required String transactionRaw}) async =>
206
call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
207
.then((dynamic result) {
208
- print('result $result');
208
if (result is String) {
209
return result;
210
}
@@ -264,6 +263,25 @@ class ElectrumClient {
263
return completer.future;
264
}
265
266
+ Future<dynamic> callWithTimeout(
267
+ {String method,
268
+ List<Object> params = const [],
269
+ int timeout = 2000}) async {
270
+ final completer = Completer<dynamic>();
271
+ _id += 1;
272
+ final id = _id;
273
+ _regisryTask(id, completer);
274
+ socket.write(jsonrpc(method: method, id: _id, params: params));
275
+
276
+ Timer(Duration(milliseconds: timeout), () {
277
+ if (!completer.isCompleted) {
278
+ completer.completeError(RequestFailedTimeoutException(method, _id));
279
+ }
280
+ });
281
+
282
+ return completer.future;
283
+ }
284
+
285
void request({String method, List<Object> params = const []}) {
286
_id += 1;
287
socket.write(jsonrpc(method: method, id: _id, params: params));
@@ -280,7 +298,9 @@ class ElectrumClient {
298
return;
299
}
300
283
- _tasks[id]?.completer?.complete(data);
301
+ if (!(_tasks[id]?.completer?.isCompleted ?? false)) {
302
+ _tasks[id]?.completer?.complete(data);
303
+ }
304
305
if (!(_tasks[id]?.isSubscription ?? false)) {
306
_tasks[id] = null;
@@ -303,4 +323,19 @@ class ElectrumClient {
323
break;
324
}
325
}
326
+
327
+ void _setIsConnected(bool isConnected) {
328
+ if (_isConnected != isConnected) {
329
+ onConnectionStatusChange?.call(isConnected);
330
+ }
331
+
332
+ _isConnected = isConnected;
333
+ }
334
+}
335
+
336
+class RequestFailedTimeoutException implements Exception {
337
+ RequestFailedTimeoutException(this.method, this.id);
338
+
339
+ final String method;
340
+ final int id;
341
}
lib/reactions/bootstrap.dart
+14
@@ -1,3 +1,5 @@
1
+import 'dart:async';
2
+
3
import 'package:cake_wallet/core/key_service.dart';
4
import 'package:cake_wallet/src/domain/common/sync_status.dart';
5
import 'package:mobx/mobx.dart';
@@ -49,6 +51,7 @@ ReactionDisposer _initialAuthReaction;
51
ReactionDisposer _onCurrentWalletChangeReaction;
52
ReactionDisposer _onWalletSyncStatusChangeReaction;
53
ReactionDisposer _onCurrentFiatCurrencyChangeDisposer;
54
+Timer _reconnectionTimer;
55
56
Future<void> bootstrap(
57
{FiatConvertationService fiatConvertationService}) async {
@@ -74,10 +77,21 @@ Future<void> bootstrap(
77
_onCurrentWalletChangeReaction ??=
78
reaction((_) => getIt.get<AppStore>().wallet, (WalletBase wallet) async {
79
_onWalletSyncStatusChangeReaction?.reaction?.dispose();
80
+ _reconnectionTimer?.cancel();
81
_onWalletSyncStatusChangeReaction = reaction(
82
(_) => wallet.syncStatus is ConnectedSyncStatus,
83
(_) async => await wallet.startSync());
84
85
+ _reconnectionTimer = Timer.periodic(Duration(seconds: 5), (_) async {
86
+ if (wallet.syncStatus is LostConnectionSyncStatus ||
87
+ wallet.syncStatus is FailedSyncStatus) {
88
+ try {
89
+ await wallet.connectToNode(
90
+ node: settingsStore.getCurrentNode(wallet.type));
91
+ } catch (_) {}
92
+ }
93
+ });
94
+
95
await getIt
96
.get<SharedPreferences>()
97
.setString('current_wallet_name', wallet.name);
lib/src/domain/common/sync_status.dart
+8
@@ -73,3 +73,11 @@ class ConnectedSyncStatus extends SyncStatus {
73
@override
74
String title() => S.current.sync_status_connected;
75
}
76
+
77
+class LostConnectionSyncStatus extends SyncStatus {
78
+ @override
79
+ double progress() => 1.0;
80
+
81
+ @override
82
+ String title() => S.current.sync_status_failed_connect;
83
+}
\ No newline at end of file
lib/src/domain/common/transaction_info.dart
+1
@@ -7,6 +7,7 @@ abstract class TransactionInfo extends Object {
7
bool isPending;
8
DateTime date;
9
int height;
10
+ int confirmations;
11
String amountFormatted();
12
String fiatAmount();
13
void changeFiatAmount(String amount);
lib/src/screens/send/send_page.dart
+10
-2
@@ -159,7 +159,8 @@ class SendFormState extends State<SendForm> {
159
decoration: InputDecoration(
160
prefixIcon: Padding(
161
padding: EdgeInsets.only(top: 12),
162
- child: Text('${widget.sendViewModel.currency.toString()}:',
162
+ child: Text(
163
+ '${widget.sendViewModel.currency.toString()}:',
164
style: TextStyle(
165
fontSize: 16,
166
fontWeight: FontWeight.w500,
@@ -213,7 +214,14 @@ class SendFormState extends State<SendForm> {
214
borderSide: BorderSide(
215
color: Theme.of(context).dividerColor,
216
width: 1.0))),
216
- validator: widget.sendViewModel.amountValidator),
217
+ validator: (String value) {
218
+ if (widget.sendViewModel.all) {
219
+ return null;
220
+ }
221
+
222
+ return widget.sendViewModel.amountValidator
223
+ .call(value);
224
+ }),
225
),
226
Padding(
227
padding: const EdgeInsets.only(top: 20),
lib/src/screens/transaction_details/transaction_details_page.dart
+4
@@ -1,3 +1,4 @@
1
+
2
import 'package:intl/intl.dart';
3
import 'package:flutter/material.dart';
4
import 'package:flutter/services.dart';
@@ -48,6 +49,9 @@ class TransactionDetailsPage extends BasePage {
49
StandartListItem(
50
title: S.current.transaction_details_date,
51
value: dateFormat.format(tx.date)),
52
+ StandartListItem(
53
+ title: 'Confirmations',
54
+ value: tx.confirmations?.toString()),
55
StandartListItem(
56
title: S.current.transaction_details_height, value: '${tx.height}'),
57
StandartListItem(
lib/view_model/dashboard/dashboard_view_model.dart
+1
-1
@@ -86,7 +86,7 @@ abstract class DashboardViewModelBase with Store {
86
statusText = S.current.Blocks_remaining(status.toString());
87
}
88
89
- if (status is FailedSyncStatus) {
89
+ if (status is FailedSyncStatus || status is LostConnectionSyncStatus) {
90
statusText = S.current.please_try_to_connect_to_another_node;
91
}
92
lib/view_model/send/send_view_model.dart
+5
@@ -116,6 +116,11 @@ abstract class SendViewModelBase with Store {
116
117
@action
118
void setCryptoAmount(String amount) {
119
+ // FIXME: hardcoded value.
120
+ if (amount.toUpperCase() != 'ALL') {
121
+ all = false;
122
+ }
123
+
124
cryptoAmount = amount;
125
_updateFiatAmount();
126
}