Fixes
M committed
Sep 26, 2020 at 22:17 UTC
51cf11127c54149022b22c626cb3ba2207b5c742
21 files changed
+353
-245
lib/core/contact_service.dart
+2
-2
@@ -22,7 +22,7 @@ class ContactService {
22
if (index >= 0) {
23
_forceUpdateContactListStore();
24
} else {
25
- contactListStore.contacts.add(contact);
25
+ // contactListStore.contacts.add(contact);
26
}
27
}
28
@@ -33,6 +33,6 @@ class ContactService {
33
34
void _forceUpdateContactListStore() {
35
contactListStore.contacts.clear();
36
- contactListStore.contacts.addAll(contactSource.values);
36
+ // contactListStore.contacts.addAll(contactSource.values);
37
}
38
}
lib/di.dart
+10
-7
@@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
2
import 'package:cake_wallet/core/contact_service.dart';
3
import 'package:cake_wallet/core/wallet_service.dart';
4
import 'package:cake_wallet/entities/biometric_auth.dart';
5
+import 'package:cake_wallet/entities/contact_record.dart';
6
import 'package:cake_wallet/monero/monero_wallet_service.dart';
7
import 'package:cake_wallet/entities/contact.dart';
8
import 'package:cake_wallet/entities/node.dart';
@@ -197,7 +198,8 @@ Future setup(
198
getIt
199
.registerFactoryParam<AuthPage, void Function(bool, AuthPageState), bool>(
200
(onAuthFinished, closable) => AuthPage(getIt.get<AuthViewModel>(),
200
- onAuthenticationFinished: onAuthFinished, closable: closable ?? false));
201
+ onAuthenticationFinished: onAuthFinished,
202
+ closable: closable ?? false));
203
204
getIt.registerFactory<DashboardPage>(() => DashboardPage(
205
walletViewModel: getIt.get<DashboardViewModel>(),
@@ -282,8 +284,8 @@ Future setup(
284
285
getIt.registerFactory(() => WalletKeysPage(getIt.get<WalletKeysViewModel>()));
286
285
- getIt.registerFactoryParam<ContactViewModel, Contact, void>(
286
- (Contact contact, _) => ContactViewModel(
287
+ getIt.registerFactoryParam<ContactViewModel, ContactRecord, void>(
288
+ (ContactRecord contact, _) => ContactViewModel(
289
contactSource, getIt.get<AppStore>().wallet,
290
contact: contact));
291
@@ -296,13 +298,14 @@ Future setup(
298
(bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(),
299
isEditable: isEditable));
300
299
- getIt.registerFactoryParam<ContactPage, Contact, void>((Contact contact, _) =>
300
- ContactPage(getIt.get<ContactViewModel>(param1: contact)));
301
+ getIt.registerFactoryParam<ContactPage, ContactRecord, void>(
302
+ (ContactRecord contact, _) =>
303
+ ContactPage(getIt.get<ContactViewModel>(param1: contact)));
304
305
getIt.registerFactory(() {
306
final appStore = getIt.get<AppStore>();
304
- return NodeListViewModel(appStore.nodeListStore, nodeSource,
305
- appStore.wallet, appStore.settingsStore);
307
+ return NodeListViewModel(
308
+ nodeSource, appStore.wallet, appStore.settingsStore);
309
});
310
311
getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));
lib/entities/contact_record.dart
new
+40
@@ -0,0 +1,40 @@
1
+import 'package:hive/hive.dart';
2
+import 'package:mobx/mobx.dart';
3
+import 'package:cake_wallet/entities/contact.dart';
4
+import 'package:cake_wallet/entities/crypto_currency.dart';
5
+import 'package:cake_wallet/entities/record.dart';
6
+
7
+part 'contact_record.g.dart';
8
+
9
+class ContactRecord = ContactRecordBase with _$ContactRecord;
10
+
11
+abstract class ContactRecordBase extends Record<Contact> with Store {
12
+ ContactRecordBase(Box<Contact> source, Contact original)
13
+ : super(source, original);
14
+
15
+ @observable
16
+ String name;
17
+
18
+ @observable
19
+ String address;
20
+
21
+ @observable
22
+ CryptoCurrency type;
23
+
24
+ @override
25
+ void toBind(Contact original) {
26
+ reaction((_) => name, (String name) => original.name = name);
27
+ reaction((_) => address, (String address) => original.address = address);
28
+ reaction(
29
+ (_) => type,
30
+ (CryptoCurrency currency) =>
31
+ original.updateCryptoCurrency(currency: currency));
32
+ }
33
+
34
+ @override
35
+ void fromBind(Contact original) {
36
+ name = original.name;
37
+ address = original.address;
38
+ type = original.type;
39
+ }
40
+}
lib/entities/node.dart
+3
@@ -38,6 +38,9 @@ class Node extends HiveObject with Keyable {
38
@HiveField(3)
39
int typeRaw;
40
41
+ @override
42
+ dynamic get keyIndex => key;
43
+
44
WalletType get type => deserializeFromInt(typeRaw);
45
46
set type(WalletType type) => typeRaw = serializeToInt(type);
lib/entities/record.dart
new
+35
@@ -0,0 +1,35 @@
1
+import 'dart:async';
2
+
3
+import 'package:cake_wallet/utils/mobx.dart';
4
+import 'package:hive/hive.dart';
5
+
6
+abstract class Record<T extends HiveObject> with Keyable {
7
+ Record(this._source, this.original) {
8
+ _listener?.cancel();
9
+ _listener = _source.watch(key: original.key).listen((event) {
10
+ if (!event.deleted) {
11
+ fromBind(event.value as T);
12
+ }
13
+ });
14
+
15
+ fromBind(original);
16
+ toBind(original);
17
+ }
18
+
19
+ dynamic get key => original.key;
20
+
21
+ @override
22
+ dynamic get keyIndex => key;
23
+
24
+ final T original;
25
+
26
+ final Box<T> _source;
27
+
28
+ StreamSubscription<BoxEvent> _listener;
29
+
30
+ void fromBind(T original);
31
+
32
+ void toBind(T original);
33
+
34
+ Future<void> save() => original.save();
35
+}
lib/reactions/bootstrap.dart
+2
@@ -1,4 +1,5 @@
1
import 'dart:async';
2
+import 'package:cake_wallet/reactions/on_current_node_change.dart';
3
import 'package:flutter/cupertino.dart';
4
import 'package:flutter/widgets.dart';
5
import 'package:shared_preferences/shared_preferences.dart';
@@ -31,4 +32,5 @@ Future<void> bootstrap(GlobalKey<NavigatorState> navigatorKey) async {
32
startCurrentWalletChangeReaction(
33
appStore, settingsStore, fiatConversionStore);
34
startCurrentFiatChangeReaction(appStore, settingsStore);
35
+ startOnCurrentNodeChangeReaction(appStore);
36
}
lib/reactions/on_current_node_change.dart
new
+17
@@ -0,0 +1,17 @@
1
+import 'package:mobx/mobx.dart';
2
+import 'package:cake_wallet/entities/node.dart';
3
+import 'package:cake_wallet/store/app_store.dart';
4
+
5
+ReactionDisposer _onCurrentNodeChangeReaction;
6
+
7
+void startOnCurrentNodeChangeReaction(AppStore appStore) {
8
+ _onCurrentNodeChangeReaction?.reaction?.dispose();
9
+ _onCurrentNodeChangeReaction =
10
+ reaction((_) => appStore.settingsStore.currentNode, (Node node) async {
11
+ try {
12
+ await appStore.wallet.connectToNode(node: node);
13
+ } catch (e) {
14
+ print(e.toString());
15
+ }
16
+ });
17
+}
lib/router.dart
+2
-1
@@ -1,3 +1,4 @@
1
+import 'package:cake_wallet/entities/contact_record.dart';
2
import 'package:flutter/cupertino.dart';
3
import 'package:flutter/material.dart';
4
import 'package:cake_wallet/routes.dart';
@@ -252,7 +253,7 @@ class Router {
253
case Routes.addressBookAddContact:
254
return CupertinoPageRoute<void>(
255
builder: (_) =>
255
- getIt.get<ContactPage>(param1: settings.arguments as Contact));
256
+ getIt.get<ContactPage>(param1: settings.arguments as ContactRecord));
257
258
case Routes.showKeys:
259
return MaterialPageRoute<void>(
lib/src/screens/contact/contact_list_page.dart
+6
-6
@@ -164,17 +164,17 @@ class ContactListPage extends BasePage {
164
final isDelete =
165
await showAlertDialog(context) ?? false;
166
167
- if (isDelete) {
168
- await contactListViewModel
169
- .delete(contact);
170
- }
167
+ // if (isDelete) {
168
+ // await contactListViewModel
169
+ // .delete(contact);
170
+ // }
171
},
172
),
173
],
174
dismissal: SlidableDismissal(
175
child: SlidableDrawerDismissal(),
176
- onDismissed: (actionType) async =>
177
- await contactListViewModel.delete(contact),
176
+ onDismissed: (actionType) async => null,
177
+ // await contactListViewModel.delete(contact),
178
onWillDismiss: (actionType) async =>
179
showAlertDialog(context),
180
),
lib/src/screens/monero_accounts/widgets/account_tile.dart
+1
-1
@@ -31,7 +31,7 @@ class AccountTile extends StatelessWidget {
31
accountName,
32
style: TextStyle(
33
fontSize: 18,
34
- fontWeight: FontWeight.bold,
34
+ fontWeight: FontWeight.w600,
35
fontFamily: 'Poppins',
36
color: textColor,
37
decoration: TextDecoration.none,
lib/src/screens/nodes/nodes_list_page.dart
+76
-77
@@ -67,87 +67,86 @@ class NodeListPage extends BasePage {
67
sectionCount: 2,
68
context: context,
69
itemBuilder: (_, sectionIndex, index) {
70
- if (sectionIndex == 0) {
71
- return NodeHeaderListRow(
72
- title: S.of(context).add_new_node,
73
- onTap: (_) async =>
74
- await Navigator.of(context).pushNamed(Routes.newNode));
75
- }
70
+ return Observer(builder: (_) {
71
+ if (sectionIndex == 0) {
72
+ return NodeHeaderListRow(
73
+ title: S.of(context).add_new_node,
74
+ onTap: (_) async => await Navigator.of(context)
75
+ .pushNamed(Routes.newNode));
76
+ }
77
77
- final node = nodeListViewModel.nodes[index];
78
- final nodeListRow = NodeListRow(
79
- title: node.value.uri,
80
- isSelected: node.isSelected,
81
- isAlive: node.value.requestNode(),
82
- onTap: (_) async {
83
- if (node.isSelected) {
84
- return;
85
- }
78
+ final node = nodeListViewModel.nodes[index];
79
+ final isSelected = node.keyIndex ==
80
+ nodeListViewModel.settingsStore.currentNode.keyIndex;
81
+ final nodeListRow = NodeListRow(
82
+ title: node.uri,
83
+ isSelected: isSelected,
84
+ isAlive: node.requestNode(),
85
+ onTap: (_) async {
86
+ if (isSelected) {
87
+ return;
88
+ }
89
87
- await showPopUp<void>(
88
- context: context,
89
- builder: (BuildContext context) {
90
- return AlertDialog(
91
- content: Text(
92
- S.of(context).change_current_node(node.value.uri),
93
- textAlign: TextAlign.center,
94
- ),
95
- actions: <Widget>[
96
- FlatButton(
97
- onPressed: () => Navigator.pop(context),
98
- child: Text(S.of(context).cancel)),
99
- FlatButton(
100
- onPressed: () async {
101
- Navigator.of(context).pop();
102
- await nodeListViewModel
103
- .setAsCurrent(node.value);
104
- },
105
- child: Text(S.of(context).change)),
106
- ],
107
- );
108
- });
109
- });
90
+ await showPopUp<void>(
91
+ context: context,
92
+ builder: (BuildContext context) {
93
+ // FIXME: Add translation.
94
+ return AlertWithTwoActions(
95
+ alertTitle: 'Change current node',
96
+ alertContent:
97
+ S.of(context).change_current_node(node.uri),
98
+ leftButtonText: S.of(context).cancel,
99
+ rightButtonText: S.of(context).change,
100
+ actionLeftButton: () =>
101
+ Navigator.of(context).pop(),
102
+ actionRightButton: () async {
103
+ await nodeListViewModel.setAsCurrent(node);
104
+ Navigator.of(context).pop();
105
+ });
106
+ });
107
+ });
108
111
- final dismissibleRow = Dismissible(
112
- key: Key('${node.keyIndex}'),
113
- confirmDismiss: (direction) async {
114
- return await showPopUp(
115
- context: context,
116
- builder: (BuildContext context) {
117
- return AlertWithTwoActions(
118
- alertTitle: S.of(context).remove_node,
119
- alertContent: S.of(context).remove_node_message,
120
- rightButtonText: S.of(context).remove,
121
- leftButtonText: S.of(context).cancel,
122
- actionRightButton: () =>
123
- Navigator.pop(context, true),
124
- actionLeftButton: () =>
125
- Navigator.pop(context, false));
126
- });
127
- },
128
- onDismissed: (direction) async =>
129
- nodeListViewModel.delete(node.value),
130
- direction: DismissDirection.endToStart,
131
- background: Container(
132
- padding: EdgeInsets.only(right: 10.0),
133
- alignment: AlignmentDirectional.centerEnd,
134
- color: Palette.red,
135
- child: Column(
136
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
137
- children: <Widget>[
138
- const Icon(
139
- CupertinoIcons.delete,
140
- color: Colors.white,
141
- ),
142
- Text(
143
- S.of(context).delete,
144
- style: TextStyle(color: Colors.white),
145
- )
146
- ],
147
- )),
148
- child: nodeListRow);
109
+ final dismissibleRow = Dismissible(
110
+ key: Key('${node.keyIndex}'),
111
+ confirmDismiss: (direction) async {
112
+ return await showPopUp(
113
+ context: context,
114
+ builder: (BuildContext context) {
115
+ return AlertWithTwoActions(
116
+ alertTitle: S.of(context).remove_node,
117
+ alertContent: S.of(context).remove_node_message,
118
+ rightButtonText: S.of(context).remove,
119
+ leftButtonText: S.of(context).cancel,
120
+ actionRightButton: () =>
121
+ Navigator.pop(context, true),
122
+ actionLeftButton: () =>
123
+ Navigator.pop(context, false));
124
+ });
125
+ },
126
+ onDismissed: (direction) async =>
127
+ nodeListViewModel.delete(node),
128
+ direction: DismissDirection.endToStart,
129
+ background: Container(
130
+ padding: EdgeInsets.only(right: 10.0),
131
+ alignment: AlignmentDirectional.centerEnd,
132
+ color: Palette.red,
133
+ child: Column(
134
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
135
+ children: <Widget>[
136
+ const Icon(
137
+ CupertinoIcons.delete,
138
+ color: Colors.white,
139
+ ),
140
+ Text(
141
+ S.of(context).delete,
142
+ style: TextStyle(color: Colors.white),
143
+ )
144
+ ],
145
+ )),
146
+ child: nodeListRow);
147
150
- return node.isSelected ? nodeListRow : dismissibleRow;
148
+ return isSelected ? nodeListRow : dismissibleRow;
149
+ });
150
},
151
itemCounter: (int sectionIndex) {
152
if (sectionIndex == 0) {
lib/store/contact_list_store.dart
+3
-3
@@ -1,12 +1,12 @@
1
import 'package:mobx/mobx.dart';
2
-import 'package:cake_wallet/entities/contact.dart';
2
+import 'package:cake_wallet/entities/contact_record.dart';
3
4
part 'contact_list_store.g.dart';
5
6
class ContactListStore = ContactListStoreBase with _$ContactListStore;
7
8
abstract class ContactListStoreBase with Store {
9
- ContactListStoreBase() : contacts = ObservableList<Contact>();
9
+ ContactListStoreBase() : contacts = ObservableList<ContactRecord>();
10
11
- final ObservableList<Contact> contacts;
11
+ final ObservableList<ContactRecord> contacts;
12
}
lib/store/node_list_store.dart
+3
-7
@@ -22,17 +22,13 @@ abstract class NodeListStoreBase with Store {
22
23
final nodeSource = getIt.get<Box<Node>>();
24
_instance = NodeListStore();
25
- _instance.replaceValues(nodeSource.values);
25
+ _instance.nodes.clear();
26
+ _instance.nodes.addAll(nodeSource.values);
27
_onNodesSourceChange?.cancel();
27
- _onNodesSourceChange = bindBox(nodeSource, _instance.nodes);
28
+ _onNodesSourceChange = nodeSource.bindToList(_instance.nodes);
29
30
return _instance;
31
}
32
33
final ObservableList<Node> nodes;
33
-
34
- void replaceValues(Iterable<Node> newNodes) {
35
- nodes.clear();
36
- nodes.addAll(newNodes);
37
- }
34
}
lib/store/settings_store.dart
+6
-7
@@ -21,7 +21,6 @@ class SettingsStore = SettingsStoreBase with _$SettingsStore;
21
abstract class SettingsStoreBase with Store {
22
SettingsStoreBase(
23
{@required SharedPreferences sharedPreferences,
24
- @required Box<Node> nodeSource,
24
@required FiatCurrency initialFiatCurrency,
25
@required TransactionPriority initialTransactionPriority,
26
@required BalanceDisplayMode initialBalanceDisplayMode,
@@ -43,10 +42,9 @@ abstract class SettingsStoreBase with Store {
42
pinCodeLength = initialPinLength;
43
languageCode = initialLanguageCode;
44
currentLocale = initialCurrentLocale;
46
- itemHeaders = {};
45
+ currentNode = nodes[WalletType.monero];
46
this.nodes = ObservableMap<WalletType, Node>.of(nodes);
47
_sharedPreferences = sharedPreferences;
49
- _nodeSource = nodeSource;
48
49
reaction(
50
(_) => allowBiometricalAuthentication,
@@ -58,6 +56,9 @@ abstract class SettingsStoreBase with Store {
56
(_) => pinCodeLength,
57
(int pinLength) => sharedPreferences.setInt(
58
PreferencesKey.currentPinLength, pinLength));
59
+
60
+ reaction((_) => currentNode,
61
+ (Node node) => _saveCurrentNode(node, WalletType.monero));
62
}
63
64
static const defaultPinLength = 4;
@@ -88,7 +89,7 @@ abstract class SettingsStoreBase with Store {
89
int pinCodeLength;
90
91
@observable
91
- Map<String, String> itemHeaders;
92
+ Node currentNode;
93
94
String languageCode;
95
@@ -97,7 +98,6 @@ abstract class SettingsStoreBase with Store {
98
String appVersion;
99
100
SharedPreferences _sharedPreferences;
100
- Box<Node> _nodeSource;
101
102
ObservableMap<WalletType, Node> nodes;
103
@@ -150,7 +150,6 @@ abstract class SettingsStoreBase with Store {
150
WalletType.monero: moneroNode,
151
WalletType.bitcoin: bitcoinElectrumServer
152
},
153
- nodeSource: nodeSource,
153
appVersion: packageInfo.version,
154
initialFiatCurrency: currentFiatCurrency,
155
initialTransactionPriority: currentTransactionPriority,
@@ -164,7 +163,7 @@ abstract class SettingsStoreBase with Store {
163
initialCurrentLocale: initialCurrentLocale);
164
}
165
167
- Future<void> setCurrentNode(Node node, WalletType walletType) async {
166
+ Future<void> _saveCurrentNode(Node node, WalletType walletType) async {
167
switch (walletType) {
168
case WalletType.bitcoin:
169
await _sharedPreferences.setInt(
lib/utils/item_cell.dart
+9
-2
@@ -1,11 +1,18 @@
1
import 'package:flutter/foundation.dart';
2
+import 'package:mobx/mobx.dart';
3
import 'package:cake_wallet/utils/mobx.dart';
4
5
+// part 'node_list_view_model.g.dart';
6
+//
7
+// class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel;
8
+
9
class ItemCell<Item> with Keyable {
5
- ItemCell(this.value, {@required this.isSelected, @required dynamic key}) {
10
+ ItemCell(this.value, {this.isSelectedBuilder, @required dynamic key}) {
11
keyIndex = key;
12
}
13
14
final Item value;
10
- final bool isSelected;
15
+
16
+ bool get isSelected => isSelectedBuilder(value);
17
+ bool Function(Item item) isSelectedBuilder;
18
}
lib/utils/mobx.dart
+100
-60
@@ -6,36 +6,6 @@ mixin Keyable {
6
dynamic keyIndex;
7
}
8
9
-void connectWithTransform<T extends Keyable, Y extends Keyable>(
10
- ObservableList<T> source, ObservableList<Y> dest, Y Function(T) transform,
11
- {bool Function(T) filter}) {
12
- source.observe((ListChange<T> change) {
13
- change.elementChanges.forEach((change) {
14
- switch (change.type) {
15
- case OperationType.add:
16
- if (filter?.call(change.newValue as T) ?? true) {
17
- dest.add(transform(change.newValue as T));
18
- }
19
- break;
20
- case OperationType.remove:
21
- // Hive could has equal index and key
22
- dest.removeWhere(
23
- (elem) => elem.keyIndex == (change.oldValue.key ?? change.index));
24
- break;
25
- case OperationType.update:
26
- for (var i = 0; i < dest.length; i++) {
27
- final item = dest[i];
28
-
29
- if (item.keyIndex == change.newValue.key) {
30
- dest[i] = transform(change.newValue as T);
31
- }
32
- }
33
- break;
34
- }
35
- });
36
- });
37
-}
38
-
9
void connectMapToListWithTransform<T extends Keyable, Y extends Keyable>(
10
ObservableMap<dynamic, T> source,
11
ObservableList<Y> dest,
@@ -50,8 +20,8 @@ void connectMapToListWithTransform<T extends Keyable, Y extends Keyable>(
20
break;
21
case OperationType.remove:
22
// Hive could has equal index and key
53
- dest.removeWhere(
54
- (elem) => elem.keyIndex == (change.key ?? change.newValue.keyIndex));
23
+ dest.removeWhere((elem) =>
24
+ elem.keyIndex == (change.key ?? change.newValue.keyIndex));
25
break;
26
case OperationType.update:
27
for (var i = 0; i < dest.length; i++) {
@@ -66,54 +36,124 @@ void connectMapToListWithTransform<T extends Keyable, Y extends Keyable>(
36
});
37
}
38
69
-void connect<T extends Keyable>(
70
- ObservableList<T> source, ObservableList<T> dest) {
71
- source.observe((ListChange<T> change) {
72
- source.observe((ListChange<T> change) {
39
+typedef Filter<T> = bool Function(T);
40
+typedef Transform<T, Y> = Y Function(T);
41
+
42
+enum ChangeType { update, delete, add }
43
+
44
+class EntityChange<T extends Keyable> {
45
+ EntityChange(this.value, this.type, {dynamic key}) : _key = key;
46
+
47
+ dynamic get key => _key ?? value.keyIndex;
48
+ final T value;
49
+ final ChangeType type;
50
+ final dynamic _key;
51
+}
52
+
53
+extension MobxBindable<T extends Keyable> on Box<T> {
54
+ StreamSubscription<BoxEvent> bindToList(
55
+ ObservableList<T> dest, {
56
+ bool initialFire = false,
57
+ Filter<T> filter,
58
+ }) {
59
+ if (initialFire) {
60
+ dest.addAll(values);
61
+ }
62
+
63
+ return watch().listen((event) {
64
+ if (filter != null && !filter(event.value as T)) {
65
+ return;
66
+ }
67
+
68
+ dest.acceptBoxChange(event);
69
+ });
70
+ }
71
+
72
+ StreamSubscription<BoxEvent> bindToListWithTransform<Y extends Keyable>(
73
+ ObservableList<Y> dest,
74
+ Transform<T, Y> transform, {
75
+ bool initialFire = false,
76
+ Filter<T> filter,
77
+ }) {
78
+ if (initialFire) {
79
+ dest.addAll(values.map((value) => transform(value)));
80
+ }
81
+
82
+ return watch().listen((event) {
83
+ if (filter != null && !filter(event.value as T)) {
84
+ return;
85
+ }
86
+
87
+ dest.acceptBoxChange(event, transformed: transform(event.value as T));
88
+ });
89
+ }
90
+}
91
+
92
+extension HiveBindable<T extends Keyable> on ObservableList<T> {
93
+ Stream<EntityChange<T>> listen() {
94
+ // ignore: close_sinks
95
+ final controller = StreamController<EntityChange<T>>();
96
+
97
+ observe((ListChange<T> change) {
98
change.elementChanges.forEach((change) {
99
+ ChangeType type;
100
+
101
switch (change.type) {
102
case OperationType.add:
76
- // if (filter?.call(change.newValue as T) ?? true) {
77
- dest.add(change.newValue as T);
78
- // }
103
+ type = ChangeType.add;
104
break;
105
case OperationType.remove:
81
- // Hive could has equal index and key
82
- dest.removeWhere((elem) =>
83
- elem.keyIndex == (change.oldValue.key ?? change.index));
106
+ type = ChangeType.delete;
107
break;
108
case OperationType.update:
86
- for (var i = 0; i < dest.length; i++) {
87
- final item = dest[i];
88
-
89
- if (item.keyIndex == change.newValue.key) {
90
- dest[i] = change.newValue as T;
91
- }
92
- }
109
+ type = ChangeType.update;
110
break;
111
}
112
+
113
+ final value = change.newValue as T;
114
+ controller.add(EntityChange(value, type));
115
});
116
});
97
- });
98
-}
117
100
-StreamSubscription<BoxEvent> bindBox<T extends Keyable>(
101
- Box<T> source, ObservableList<T> dest) {
102
- return source.watch().listen((event) {
118
+ return controller.stream;
119
+ }
120
+
121
+ StreamSubscription<EntityChange<T>> bindToList(ObservableList<T> dest) =>
122
+ listen().listen((event) => dest.acceptEntityChange(event));
123
+
124
+ void acceptBoxChange(BoxEvent event, {T transformed}) {
125
if (event.deleted) {
104
- dest.removeWhere((el) => el.keyIndex == event.key);
126
+ removeWhere((el) => el.keyIndex == event.key);
127
+ }
128
+
129
+ final dynamic value = transformed ?? event.value;
130
+
131
+ if (value is T) {
132
+ final index = indexWhere((el) => el.keyIndex == value.keyIndex);
133
+
134
+ if (index > -1) {
135
+ this.setAll(index, [value]); // FIXME: fixme
136
+ } else {
137
+ add(value);
138
+ }
139
+ }
140
+ }
141
+
142
+ void acceptEntityChange(EntityChange<T> event) {
143
+ if (event.type == ChangeType.delete) {
144
+ removeWhere((el) => el.keyIndex == event.key);
145
}
146
147
final dynamic value = event.value;
148
149
if (value is T) {
110
- final elIndex = dest.indexWhere((el) => el.keyIndex == value.keyIndex);
150
+ final index = indexWhere((el) => el.keyIndex == value.keyIndex);
151
112
- if (elIndex > -1) {
113
- dest[elIndex] = value;
152
+ if (index > -1) {
153
+ this.setAll(index, [value]); // FIXME: fixme
154
} else {
115
- dest.add(value);
155
+ add(value);
156
}
157
}
118
- });
158
+ }
159
}
lib/view_model/contact_list/contact_list_view_model.dart
+6
-3
@@ -1,4 +1,5 @@
1
import 'dart:async';
2
+import 'package:cake_wallet/entities/contact_record.dart';
3
import 'package:hive/hive.dart';
4
import 'package:mobx/mobx.dart';
5
import 'package:cake_wallet/core/contact_service.dart';
@@ -14,19 +15,21 @@ class ContactListViewModel = ContactListViewModelBase
15
abstract class ContactListViewModelBase with Store {
16
ContactListViewModelBase(
17
this.addressBookStore, this.contactService, this.contactSource) {
17
- _subscription = bindBox(contactSource, addressBookStore.contacts);
18
+ _subscription = contactSource.bindToListWithTransform(addressBookStore.contacts,
19
+ (Contact contact) => ContactRecord(contactSource, contact),
20
+ initialFire: true);
21
}
22
23
final ContactListStore addressBookStore;
24
final ContactService contactService;
25
final Box<Contact> contactSource;
26
24
- ObservableList<Contact> get contacts => addressBookStore.contacts;
27
+ ObservableList<ContactRecord> get contacts => addressBookStore.contacts;
28
29
StreamSubscription<BoxEvent> _subscription;
30
31
void dispose() {
29
- _subscription.cancel();
32
+ // _subscription.cancel();
33
}
34
35
Future<void> delete(Contact contact) async => contactService.delete(contact);
lib/view_model/contact_list/contact_view_model.dart
+8
-4
@@ -1,3 +1,4 @@
1
+import 'package:cake_wallet/entities/contact_record.dart';
2
import 'package:hive/hive.dart';
3
import 'package:mobx/mobx.dart';
4
import 'package:cake_wallet/core/execution_state.dart';
@@ -11,7 +12,7 @@ part 'contact_view_model.g.dart';
12
class ContactViewModel = ContactViewModelBase with _$ContactViewModel;
13
14
abstract class ContactViewModelBase with Store {
14
- ContactViewModelBase(this._contacts, this._wallet, {Contact contact})
15
+ ContactViewModelBase(this._contacts, this._wallet, {ContactRecord contact})
16
: state = InitialExecutionState(),
17
currencies = CryptoCurrency.all,
18
_contact = contact {
@@ -41,7 +42,7 @@ abstract class ContactViewModelBase with Store {
42
final List<CryptoCurrency> currencies;
43
final WalletBase _wallet;
44
final Box<Contact> _contacts;
44
- final Contact _contact;
45
+ final ContactRecord _contact;
46
47
@action
48
void reset() {
@@ -57,9 +58,12 @@ abstract class ContactViewModelBase with Store {
58
if (_contact != null) {
59
_contact.name = name;
60
_contact.address = address;
60
- _contact.updateCryptoCurrency(currency: currency);
61
- await _contacts.put(_contact.key, _contact);
61
+ _contact.type = currency;
62
+ await _contact.save();
63
+ // await _contacts.put(_contact.key, _contact);
64
} else {
65
+ // final contact = ContactRecordBase.create(_contacts, name, address, currency);
66
+ // await contact.save();
67
await _contacts
68
.add(Contact(name: name, address: address, type: currency));
69
}
lib/view_model/node_list/node_list_view_model.dart
+9
-43
@@ -1,45 +1,29 @@
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/store/settings_store.dart';
5
import 'package:cake_wallet/entities/node.dart';
6
import 'package:cake_wallet/entities/node_list.dart';
6
-import 'package:cake_wallet/store/node_list_store.dart';
7
-import 'package:cake_wallet/store/settings_store.dart';
7
import 'package:cake_wallet/entities/default_settings_migration.dart';
8
import 'package:cake_wallet/entities/wallet_type.dart';
9
import 'package:cake_wallet/utils/mobx.dart';
11
-import 'package:cake_wallet/utils/item_cell.dart';
10
11
part 'node_list_view_model.g.dart';
12
13
class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel;
14
15
abstract class NodeListViewModelBase with Store {
18
- NodeListViewModelBase(
19
- this._nodeListStore, this._nodeSource, this._wallet, this._settingsStore)
20
- : nodes = ObservableList<ItemCell<Node>>() {
21
- final currentNode = _settingsStore.getCurrentNode(_wallet.type);
22
- final values = _nodeListStore.nodes;
23
- nodes.clear();
24
- nodes.addAll(values.where((Node node) => node.type == _wallet.type).map(
25
- (Node val) => ItemCell<Node>(val,
26
- isSelected: val.key == currentNode.key, key: val.key)));
27
- connectWithTransform(
28
- _nodeListStore.nodes,
29
- nodes,
30
- (Node val) => ItemCell<Node>(val,
31
- isSelected: val.key == currentNode.key, key: val.key),
32
- filter: (Node val) => val.type == _wallet.type);
33
- reaction((_) => _settingsStore.nodes[_wallet.type],
34
- (Node _) => _updateCurrentNode());
16
+ NodeListViewModelBase(this._nodeSource, this._wallet, this.settingsStore)
17
+ : nodes = ObservableList<Node>() {
18
+ _nodeSource.bindToList(nodes,
19
+ filter: (Node val) => val.type == _wallet.type, initialFire: true);
20
}
21
37
- ObservableList<ItemCell<Node>> nodes;
22
+ final ObservableList<Node> nodes;
23
+ final SettingsStore settingsStore;
24
25
final WalletBase _wallet;
26
final Box<Node> _nodeSource;
41
- final NodeListStore _nodeListStore;
42
- final SettingsStore _settingsStore;
27
28
Future<void> reset() async {
29
await resetToDefault(_nodeSource);
@@ -64,24 +48,6 @@ abstract class NodeListViewModelBase with Store {
48
49
Future<void> delete(Node node) async => _nodeSource.delete(node.key);
50
67
- Future<void> setAsCurrent(Node node) async {
68
- await _settingsStore.setCurrentNode(node, _wallet.type);
69
- _updateCurrentNode();
70
- await _wallet.connectToNode(node: node);
71
- }
72
-
73
- @action
74
- void _updateCurrentNode() {
75
- final currentNode = _settingsStore.getCurrentNode(_wallet.type);
76
-
77
- for (var i = 0; i < nodes.length; i++) {
78
- final item = nodes[i];
79
- final isSelected = item.value.key == currentNode.key;
80
-
81
- if (item.isSelected != isSelected) {
82
- nodes[i] = ItemCell<Node>(item.value,
83
- isSelected: isSelected, key: item.keyIndex);
84
- }
85
- }
86
- }
51
+ Future<void> setAsCurrent(Node node) async =>
52
+ settingsStore.currentNode = node;
53
}
pubspec.lock
+14
-21
@@ -217,7 +217,7 @@ packages:
217
name: connectivity
218
url: "https://pub.dartlang.org"
219
source: hosted
220
- version: "0.4.9+2"
220
+ version: "0.4.9+3"
221
connectivity_for_web:
222
dependency: transitive
223
description:
@@ -252,7 +252,7 @@ packages:
252
name: crypto
253
url: "https://pub.dartlang.org"
254
source: hosted
255
- version: "2.1.4"
255
+ version: "2.1.5"
256
csslib:
257
dependency: transitive
258
description:
@@ -395,7 +395,7 @@ packages:
395
name: flutter_plugin_android_lifecycle
396
url: "https://pub.dartlang.org"
397
source: hosted
398
- version: "1.0.9"
398
+ version: "1.0.11"
399
flutter_secure_storage:
400
dependency: "direct main"
401
description:
@@ -470,7 +470,7 @@ packages:
470
name: hive_generator
471
url: "https://pub.dartlang.org"
472
source: hosted
473
- version: "0.7.1"
473
+ version: "0.7.2+1"
474
html:
475
dependency: transitive
476
description:
@@ -505,7 +505,7 @@ packages:
505
name: image
506
url: "https://pub.dartlang.org"
507
source: hosted
508
- version: "2.1.12"
508
+ version: "2.1.18"
509
intl:
510
dependency: "direct main"
511
description:
@@ -533,14 +533,14 @@ packages:
533
name: json_annotation
534
url: "https://pub.dartlang.org"
535
source: hosted
536
- version: "3.0.1"
536
+ version: "3.1.0"
537
local_auth:
538
dependency: "direct main"
539
description:
540
name: local_auth
541
url: "https://pub.dartlang.org"
542
source: hosted
543
- version: "0.6.3+1"
543
+ version: "0.6.3+2"
544
logging:
545
dependency: transitive
546
description:
@@ -645,7 +645,7 @@ packages:
645
name: path_provider
646
url: "https://pub.dartlang.org"
647
source: hosted
648
- version: "1.6.17"
648
+ version: "1.6.18"
649
path_provider_linux:
650
dependency: transitive
651
description:
@@ -687,7 +687,7 @@ packages:
687
name: petitparser
688
url: "https://pub.dartlang.org"
689
source: hosted
690
- version: "2.4.0"
690
+ version: "3.0.4"
691
platform:
692
dependency: transitive
693
description:
@@ -695,13 +695,6 @@ packages:
695
url: "https://pub.dartlang.org"
696
source: hosted
697
version: "2.2.1"
698
- platform_detect:
699
- dependency: transitive
700
- description:
701
- name: platform_detect
702
- url: "https://pub.dartlang.org"
703
- source: hosted
704
- version: "1.4.0"
698
plugin_platform_interface:
699
dependency: transitive
700
description:
@@ -785,14 +778,14 @@ packages:
778
name: share
779
url: "https://pub.dartlang.org"
780
source: hosted
788
- version: "0.6.5+1"
781
+ version: "0.6.5+2"
782
shared_preferences:
783
dependency: "direct main"
784
description:
785
name: shared_preferences
786
url: "https://pub.dartlang.org"
787
source: hosted
795
- version: "0.5.11"
788
+ version: "0.5.12"
789
shared_preferences_linux:
790
dependency: transitive
791
description:
@@ -937,7 +930,7 @@ packages:
930
name: url_launcher
931
url: "https://pub.dartlang.org"
932
source: hosted
940
- version: "5.7.0"
933
+ version: "5.7.2"
934
url_launcher_linux:
935
dependency: transitive
936
description:
@@ -965,7 +958,7 @@ packages:
958
name: url_launcher_web
959
url: "https://pub.dartlang.org"
960
source: hosted
968
- version: "0.1.3+2"
961
+ version: "0.1.4+1"
962
url_launcher_windows:
963
dependency: transitive
964
description:
@@ -1021,7 +1014,7 @@ packages:
1014
name: xml
1015
url: "https://pub.dartlang.org"
1016
source: hosted
1024
- version: "3.6.1"
1017
+ version: "4.5.1"
1018
yaml:
1019
dependency: "direct main"
1020
description:
pubspec.yaml
+1
-1
@@ -14,7 +14,7 @@ description: Cake Wallet.
14
version: 1.0.5+5
15
16
environment:
17
- sdk: ">=2.2.2 <3.0.0"
17
+ sdk: ">=2.7.0 <3.0.0"
18
19
dependencies:
20
flutter: