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: