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 }