Merge redesign part 3.

M committed Sep 7, 2020 at 18:13 UTC 70ff011fc6943693dc699b8309578488eb0d57cf
13 files changed +157 -77
lib/bitcoin/electrum.dart
+1 -1
@@ -87,7 +87,7 @@ class ElectrumClient {
87
88 Future<void> ping() async {
89 try {
90 - await callWithTimeout(method: 'server.ping');
90 + // await callWithTimeout(method: 'server.ping');
91 _setIsConnected(true);
92 } on RequestFailedTimeoutException catch (_) {
93 _setIsConnected(false);
lib/core/contact_service.dart
+1 -1
@@ -12,7 +12,7 @@ class ContactService {
12
13 Future add(Contact contact) async {
14 await contactSource.add(contact);
15 - contactListStore.contacts.add(contact);
15 + // contactListStore.contacts.add(contact);
16 }
17
18 Future update(Contact contact) async {
lib/di.dart
+4 -2
@@ -294,11 +294,13 @@ Future setup(
294
295 getIt.registerFactoryParam<ContactViewModel, Contact, void>(
296 (Contact contact, _) => ContactViewModel(
297 - getIt.get<ContactService>(), getIt.get<AppStore>().wallet,
297 + contactSource, getIt.get<AppStore>().wallet,
298 contact: contact));
299
300 getIt.registerFactory(() => ContactListViewModel(
301 - getIt.get<AppStore>().contactListStore, getIt.get<ContactService>()));
301 + getIt.get<AppStore>().contactListStore,
302 + getIt.get<ContactService>(),
303 + contactSource));
304
305 getIt.registerFactory(
306 () => ContactListPage(getIt.get<ContactListViewModel>()));
lib/src/domain/common/contact.dart
+5 -1
@@ -1,11 +1,12 @@
1 import 'package:flutter/foundation.dart';
2 import 'package:hive/hive.dart';
3 import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
4 +import 'package:cake_wallet/utils/mobx.dart';
5
6 part 'contact.g.dart';
7
8 @HiveType(typeId: 0)
8 -class Contact extends HiveObject {
9 +class Contact extends HiveObject with Keyable {
10 Contact({@required this.name, @required this.address, CryptoCurrency type})
11 : raw = type?.raw;
12
@@ -22,6 +23,9 @@ class Contact extends HiveObject {
23
24 CryptoCurrency get type => CryptoCurrency.deserialize(raw: raw);
25
26 + @override
27 + dynamic get keyIndex => key;
28 +
29 @override
30 bool operator ==(Object o) => o is Contact && o.key == key;
31
lib/src/domain/common/node.dart
+2 -1
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/utils/mobx.dart';
2 import 'package:flutter/foundation.dart';
3 import 'dart:convert';
4 import 'package:http/http.dart' as http;
@@ -8,7 +9,7 @@ import 'package:cake_wallet/src/domain/common/digest_request.dart';
9 part 'node.g.dart';
10
11 @HiveType(typeId: 1)
11 -class Node extends HiveObject {
12 +class Node extends HiveObject with Keyable {
13 Node(
14 {@required this.uri,
15 @required WalletType type,
lib/src/screens/nodes/nodes_list_page.dart
+1 -1
@@ -108,7 +108,7 @@ class NodeListPage extends BasePage {
108 });
109
110 final dismissibleRow = Dismissible(
111 - key: Key('${node.value.key}'),
111 + key: Key('${node.keyIndex}'),
112 confirmDismiss: (direction) async {
113 return await showDialog(
114 context: context,
lib/store/settings_store.dart
+4 -4
@@ -44,9 +44,9 @@ abstract class SettingsStoreBase with Store {
44 languageCode = initialLanguageCode;
45 currentLocale = initialCurrentLocale;
46 itemHeaders = {};
47 + this.nodes = ObservableMap<WalletType, Node>.of(nodes);
48 _sharedPreferences = sharedPreferences;
49 _nodeSource = nodeSource;
49 - _nodes = nodes;
50 }
51
52 static const currentNodeIdKey = 'current_node_id';
@@ -101,9 +101,9 @@ abstract class SettingsStoreBase with Store {
101 SharedPreferences _sharedPreferences;
102 Box<Node> _nodeSource;
103
104 - Map<WalletType, Node> _nodes;
104 + ObservableMap<WalletType, Node> nodes;
105
106 - Node getCurrentNode(WalletType walletType) => _nodes[walletType];
106 + Node getCurrentNode(WalletType walletType) => nodes[walletType];
107
108 Future<void> setCurrentNode(Node node, WalletType walletType) async {
109 switch (walletType) {
@@ -118,7 +118,7 @@ abstract class SettingsStoreBase with Store {
118 break;
119 }
120
121 - _nodes[walletType] = node;
121 + nodes[walletType] = node;
122 }
123
124 static Future<SettingsStore> load(
lib/utils/item_cell.dart new
+11
@@ -0,0 +1,11 @@
1 +import 'package:flutter/foundation.dart';
2 +import 'package:cake_wallet/utils/mobx.dart';
3 +
4 +class ItemCell<Item> with Keyable {
5 + ItemCell(this.value, {@required this.isSelected, @required dynamic key}) {
6 + keyIndex = key;
7 + }
8 +
9 + final Item value;
10 + final bool isSelected;
11 +}
lib/utils/mobx.dart
+79 -35
@@ -1,46 +1,90 @@
1 +import 'dart:async';
2 +
3 +import 'package:hive/hive.dart';
4 import 'package:mobx/mobx.dart';
5
3 -void connectDifferent<T, Y>(
6 +mixin Keyable {
7 + dynamic keyIndex;
8 +}
9 +
10 +void connectDifferent<T extends Keyable, Y extends Keyable>(
11 ObservableList<T> source, ObservableList<Y> dest, Y Function(T) transform,
12 {bool Function(T) filter}) {
13 source.observe((ListChange<T> change) {
7 -// switch (change.type) {
8 -// case OperationType.add:
9 -// final _values = change.added;
10 -// Iterable<T> values;
11 -
12 -// if (filter != null) {
13 -// values = _values.where(filter);
14 -// }
15 -
16 -// dest.addAll(values.map((e) => transform(e)));
17 -// break;
18 -// case OperationType.remove:
19 -// change.removed.forEach((element) {
20 -// dest.remove(element);
21 -// });
22 -
23 -// // dest.removeAt(change.index);
24 -// break;
25 -// case OperationType.update:
26 -// // change.index
27 -// break;
28 -// }
14 + change.elementChanges.forEach((change) {
15 + switch (change.type) {
16 + case OperationType.add:
17 + if (filter?.call(change.newValue as T) ?? true) {
18 + dest.add(transform(change.newValue as T));
19 + }
20 + break;
21 + case OperationType.remove:
22 + // Hive could has equal index and key
23 + dest.removeWhere(
24 + (elem) => elem.keyIndex == (change.oldValue.key ?? change.index));
25 + break;
26 + case OperationType.update:
27 + for (var i = 0; i < dest.length; i++) {
28 + final item = dest[i];
29 +
30 + if (item.keyIndex == change.newValue.key) {
31 + dest[i] = transform(change.newValue as T);
32 + }
33 + }
34 + break;
35 + }
36 + });
37 });
38 }
39
32 -void connect<T>(ObservableList<T> source, ObservableList<T> dest) {
40 +void connect<T extends Keyable>(
41 + ObservableList<T> source, ObservableList<T> dest) {
42 source.observe((ListChange<T> change) {
34 -// switch (change.type) {
35 -// case OperationType.add:
36 -// dest.addAll(change.added);
37 -// break;
38 -// case OperationType.remove:
39 -// dest.removeAt(change.index);
40 -// break;
41 -// case OperationType.update:
42 -// // change.index
43 -// break;
44 -// }
43 + source.observe((ListChange<T> change) {
44 + change.elementChanges.forEach((change) {
45 + switch (change.type) {
46 + case OperationType.add:
47 + // if (filter?.call(change.newValue as T) ?? true) {
48 + dest.add(change.newValue as T);
49 + // }
50 + break;
51 + case OperationType.remove:
52 + // Hive could has equal index and key
53 + dest.removeWhere((elem) =>
54 + elem.keyIndex == (change.oldValue.key ?? change.index));
55 + break;
56 + case OperationType.update:
57 + for (var i = 0; i < dest.length; i++) {
58 + final item = dest[i];
59 +
60 + if (item.keyIndex == change.newValue.key) {
61 + dest[i] = change.newValue as T;
62 + }
63 + }
64 + break;
65 + }
66 + });
67 + });
68 + });
69 +}
70 +
71 +StreamSubscription<BoxEvent> bindBox<T extends Keyable>(
72 + Box<T> source, ObservableList<T> dest) {
73 + return source.watch().listen((event) {
74 + if (event.deleted) {
75 + dest.removeWhere((el) => el.keyIndex == event.key);
76 + }
77 +
78 + final dynamic value = event.value;
79 +
80 + if (value is T) {
81 + final elIndex = dest.indexWhere((el) => el.keyIndex == value.keyIndex);
82 +
83 + if (elIndex > -1) {
84 + dest[elIndex] = value;
85 + } else {
86 + dest.add(value);
87 + }
88 + }
89 });
90 }
lib/view_model/contact_list/contact_list_view_model.dart
+18 -4
@@ -1,20 +1,34 @@
1 +import 'dart:async';
2 +
3 +import 'package:hive/hive.dart';
4 import 'package:mobx/mobx.dart';
5 import 'package:cake_wallet/core/contact_service.dart';
6 import 'package:cake_wallet/store/contact_list_store.dart';
7 import 'package:cake_wallet/src/domain/common/contact.dart';
8 +import 'package:cake_wallet/utils/mobx.dart';
9
10 part 'contact_list_view_model.g.dart';
11
8 -class ContactListViewModel = ContactListViewModelBase with _$ContactListViewModel;
12 +class ContactListViewModel = ContactListViewModelBase
13 + with _$ContactListViewModel;
14
15 abstract class ContactListViewModelBase with Store {
11 - ContactListViewModelBase(this.addressBookStore, this.contactService);
16 + ContactListViewModelBase(
17 + this.addressBookStore, this.contactService, this.contactSource) {
18 + _subscription = bindBox(contactSource, addressBookStore.contacts);
19 + }
20
21 final ContactListStore addressBookStore;
22 final ContactService contactService;
23 + final Box<Contact> contactSource;
24
16 - @computed
25 ObservableList<Contact> get contacts => addressBookStore.contacts;
26
27 + StreamSubscription<BoxEvent> _subscription;
28 +
29 + void dispose() {
30 + _subscription.cancel();
31 + }
32 +
33 Future<void> delete(Contact contact) async => contactService.delete(contact);
20 -}
\ No newline at end of file
34 +}
lib/view_model/contact_list/contact_view_model.dart
+12 -6
@@ -1,3 +1,4 @@
1 +import 'package:hive/hive.dart';
2 import 'package:mobx/mobx.dart';
3 import 'package:cake_wallet/core/wallet_base.dart';
4 import 'package:cake_wallet/core/contact_service.dart';
@@ -10,7 +11,7 @@ part 'contact_view_model.g.dart';
11 class ContactViewModel = ContactViewModelBase with _$ContactViewModel;
12
13 abstract class ContactViewModelBase with Store {
13 - ContactViewModelBase(this._contactService, this._wallet, {Contact contact})
14 + ContactViewModelBase(this._contacts, this._wallet, {Contact contact})
15 : state = InitialContactViewModelState(),
16 currencies = CryptoCurrency.all,
17 _contact = contact {
@@ -33,12 +34,14 @@ abstract class ContactViewModelBase with Store {
34
35 @computed
36 bool get isReady =>
36 - (name?.isNotEmpty ?? false) && (currency?.toString()?.isNotEmpty ?? false)
37 - && (address?.isNotEmpty ?? false);
37 + (name?.isNotEmpty ?? false) &&
38 + (currency?.toString()?.isNotEmpty ?? false) &&
39 + (address?.isNotEmpty ?? false);
40
41 final List<CryptoCurrency> currencies;
40 - final ContactService _contactService;
42 + // final ContactService _contactService;
43 final WalletBase _wallet;
44 + final Box<Contact> _contacts;
45 final Contact _contact;
46
47 @action
@@ -57,10 +60,13 @@ abstract class ContactViewModelBase with Store {
60 _contact.name = name;
61 _contact.address = address;
62 _contact.updateCryptoCurrency(currency: currency);
60 - await _contactService.update(_contact);
63 + await _contacts.put(_contact.key, _contact);
64 + // await _contactService.update(_contact);
65 } else {
62 - await _contactService
66 + await _contacts
67 .add(Contact(name: name, address: address, type: currency));
68 + // await _contactService
69 + // .add(Contact(name: name, address: address, type: currency));
70 }
71
72 state = ContactSavingSuccessfully();
lib/view_model/node_list/node_list_view_model.dart
+13 -17
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/utils/item_cell.dart';
2 import 'package:flutter/foundation.dart';
3 import 'package:hive/hive.dart';
4 import 'package:mobx/mobx.dart';
@@ -14,13 +15,6 @@ part 'node_list_view_model.g.dart';
15
16 class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel;
17
17 -class ItemCell<Item> {
18 - ItemCell(this.value, {@required this.isSelected});
19 -
20 - final Item value;
21 - final bool isSelected;
22 -}
23 -
18 abstract class NodeListViewModelBase with Store {
19 NodeListViewModelBase(
20 this._nodeListStore, this._nodeSource, this._wallet, this._settingsStore)
@@ -29,16 +23,16 @@ abstract class NodeListViewModelBase with Store {
23 final values = _nodeListStore.nodes;
24 nodes.clear();
25 nodes.addAll(values.where((Node node) => node.type == _wallet.type).map(
32 - (Node val) =>
33 - ItemCell<Node>(val, isSelected: val.key == currentNode.key)));
26 + (Node val) => ItemCell<Node>(val,
27 + isSelected: val.key == currentNode.key, key: val.key)));
28 connectDifferent(
29 _nodeListStore.nodes,
30 nodes,
37 - (Node val) =>
38 - ItemCell<Node>(val, isSelected: val.key == currentNode.key),
39 - filter: (Node val) {
40 - return val.type == _wallet.type;
41 - });
31 + (Node val) => ItemCell<Node>(val,
32 + isSelected: val.key == currentNode.key, key: val.key),
33 + filter: (Node val) => val.type == _wallet.type);
34 + reaction((_) => _settingsStore.nodes[_wallet.type],
35 + (Node _) => _updateCurrentNode());
36 }
37
38 ObservableList<ItemCell<Node>> nodes;
@@ -66,17 +60,18 @@ abstract class NodeListViewModelBase with Store {
60 break;
61 }
62
69 - await _wallet.connectToNode(node: node);
63 + await setAsCurrent(node);
64 }
65
66 Future<void> delete(Node node) async => _nodeSource.delete(node.key);
67
68 Future<void> setAsCurrent(Node node) async {
75 - await _wallet.connectToNode(node: node);
69 await _settingsStore.setCurrentNode(node, _wallet.type);
70 _updateCurrentNode();
71 + await _wallet.connectToNode(node: node);
72 }
73
74 + @action
75 void _updateCurrentNode() {
76 final currentNode = _settingsStore.getCurrentNode(_wallet.type);
77
@@ -85,7 +80,8 @@ abstract class NodeListViewModelBase with Store {
80 final isSelected = item.value.key == currentNode.key;
81
82 if (item.isSelected != isSelected) {
88 - nodes[i] = ItemCell<Node>(item.value, isSelected: isSelected);
83 + nodes[i] = ItemCell<Node>(item.value,
84 + isSelected: isSelected, key: item.keyIndex);
85 }
86 }
87 }
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+6 -4
@@ -67,7 +67,8 @@ abstract class WalletAddressListViewModelBase with Store {
67 WalletType get type => _wallet.type;
68
69 @computed
70 - WalletAddressListItem get address => WalletAddressListItem(address: _wallet.address);
70 + WalletAddressListItem get address =>
71 + WalletAddressListItem(address: _wallet.address);
72
73 @computed
74 PaymentURI get uri {
@@ -100,15 +101,16 @@ abstract class WalletAddressListViewModelBase with Store {
101 }
102
103 if (wallet is BitcoinWallet) {
103 - final bitcoinAddresses = wallet.addresses.map(
104 - (addr) => WalletAddressListItem(name: addr.label, address: addr.address));
104 + final bitcoinAddresses = wallet.addresses.map((addr) =>
105 + WalletAddressListItem(name: addr.label, address: addr.address));
106 addressList.addAll(bitcoinAddresses);
107 }
108
109 return addressList;
110 }
111
111 - set address(WalletAddressListItem address) => _wallet.address = address.address;
112 + set address(WalletAddressListItem address) =>
113 + _wallet.address = address.address;
114
115 bool hasAccounts;
116