This commit is contained in:
M 2020-09-26 22:17:31 +03:00
parent dcdc411d41
commit 51cf11127c
21 changed files with 362 additions and 254 deletions

View file

@ -22,7 +22,7 @@ class ContactService {
if (index >= 0) { if (index >= 0) {
_forceUpdateContactListStore(); _forceUpdateContactListStore();
} else { } else {
contactListStore.contacts.add(contact); // contactListStore.contacts.add(contact);
} }
} }
@ -33,6 +33,6 @@ class ContactService {
void _forceUpdateContactListStore() { void _forceUpdateContactListStore() {
contactListStore.contacts.clear(); contactListStore.contacts.clear();
contactListStore.contacts.addAll(contactSource.values); // contactListStore.contacts.addAll(contactSource.values);
} }
} }

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
import 'package:cake_wallet/core/contact_service.dart'; import 'package:cake_wallet/core/contact_service.dart';
import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/core/wallet_service.dart';
import 'package:cake_wallet/entities/biometric_auth.dart'; import 'package:cake_wallet/entities/biometric_auth.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/monero/monero_wallet_service.dart'; import 'package:cake_wallet/monero/monero_wallet_service.dart';
import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/node.dart';
@ -197,7 +198,8 @@ Future setup(
getIt getIt
.registerFactoryParam<AuthPage, void Function(bool, AuthPageState), bool>( .registerFactoryParam<AuthPage, void Function(bool, AuthPageState), bool>(
(onAuthFinished, closable) => AuthPage(getIt.get<AuthViewModel>(), (onAuthFinished, closable) => AuthPage(getIt.get<AuthViewModel>(),
onAuthenticationFinished: onAuthFinished, closable: closable ?? false)); onAuthenticationFinished: onAuthFinished,
closable: closable ?? false));
getIt.registerFactory<DashboardPage>(() => DashboardPage( getIt.registerFactory<DashboardPage>(() => DashboardPage(
walletViewModel: getIt.get<DashboardViewModel>(), walletViewModel: getIt.get<DashboardViewModel>(),
@ -282,8 +284,8 @@ Future setup(
getIt.registerFactory(() => WalletKeysPage(getIt.get<WalletKeysViewModel>())); getIt.registerFactory(() => WalletKeysPage(getIt.get<WalletKeysViewModel>()));
getIt.registerFactoryParam<ContactViewModel, Contact, void>( getIt.registerFactoryParam<ContactViewModel, ContactRecord, void>(
(Contact contact, _) => ContactViewModel( (ContactRecord contact, _) => ContactViewModel(
contactSource, getIt.get<AppStore>().wallet, contactSource, getIt.get<AppStore>().wallet,
contact: contact)); contact: contact));
@ -296,13 +298,14 @@ Future setup(
(bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(), (bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(),
isEditable: isEditable)); isEditable: isEditable));
getIt.registerFactoryParam<ContactPage, Contact, void>((Contact contact, _) => getIt.registerFactoryParam<ContactPage, ContactRecord, void>(
ContactPage(getIt.get<ContactViewModel>(param1: contact))); (ContactRecord contact, _) =>
ContactPage(getIt.get<ContactViewModel>(param1: contact)));
getIt.registerFactory(() { getIt.registerFactory(() {
final appStore = getIt.get<AppStore>(); final appStore = getIt.get<AppStore>();
return NodeListViewModel(appStore.nodeListStore, nodeSource, return NodeListViewModel(
appStore.wallet, appStore.settingsStore); nodeSource, appStore.wallet, appStore.settingsStore);
}); });
getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>())); getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));

View file

@ -0,0 +1,40 @@
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/crypto_currency.dart';
import 'package:cake_wallet/entities/record.dart';
part 'contact_record.g.dart';
class ContactRecord = ContactRecordBase with _$ContactRecord;
abstract class ContactRecordBase extends Record<Contact> with Store {
ContactRecordBase(Box<Contact> source, Contact original)
: super(source, original);
@observable
String name;
@observable
String address;
@observable
CryptoCurrency type;
@override
void toBind(Contact original) {
reaction((_) => name, (String name) => original.name = name);
reaction((_) => address, (String address) => original.address = address);
reaction(
(_) => type,
(CryptoCurrency currency) =>
original.updateCryptoCurrency(currency: currency));
}
@override
void fromBind(Contact original) {
name = original.name;
address = original.address;
type = original.type;
}
}

View file

@ -38,6 +38,9 @@ class Node extends HiveObject with Keyable {
@HiveField(3) @HiveField(3)
int typeRaw; int typeRaw;
@override
dynamic get keyIndex => key;
WalletType get type => deserializeFromInt(typeRaw); WalletType get type => deserializeFromInt(typeRaw);
set type(WalletType type) => typeRaw = serializeToInt(type); set type(WalletType type) => typeRaw = serializeToInt(type);

35
lib/entities/record.dart Normal file
View file

@ -0,0 +1,35 @@
import 'dart:async';
import 'package:cake_wallet/utils/mobx.dart';
import 'package:hive/hive.dart';
abstract class Record<T extends HiveObject> with Keyable {
Record(this._source, this.original) {
_listener?.cancel();
_listener = _source.watch(key: original.key).listen((event) {
if (!event.deleted) {
fromBind(event.value as T);
}
});
fromBind(original);
toBind(original);
}
dynamic get key => original.key;
@override
dynamic get keyIndex => key;
final T original;
final Box<T> _source;
StreamSubscription<BoxEvent> _listener;
void fromBind(T original);
void toBind(T original);
Future<void> save() => original.save();
}

View file

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:cake_wallet/reactions/on_current_node_change.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -31,4 +32,5 @@ Future<void> bootstrap(GlobalKey<NavigatorState> navigatorKey) async {
startCurrentWalletChangeReaction( startCurrentWalletChangeReaction(
appStore, settingsStore, fiatConversionStore); appStore, settingsStore, fiatConversionStore);
startCurrentFiatChangeReaction(appStore, settingsStore); startCurrentFiatChangeReaction(appStore, settingsStore);
startOnCurrentNodeChangeReaction(appStore);
} }

View file

@ -0,0 +1,17 @@
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/node.dart';
import 'package:cake_wallet/store/app_store.dart';
ReactionDisposer _onCurrentNodeChangeReaction;
void startOnCurrentNodeChangeReaction(AppStore appStore) {
_onCurrentNodeChangeReaction?.reaction?.dispose();
_onCurrentNodeChangeReaction =
reaction((_) => appStore.settingsStore.currentNode, (Node node) async {
try {
await appStore.wallet.connectToNode(node: node);
} catch (e) {
print(e.toString());
}
});
}

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
@ -252,7 +253,7 @@ class Router {
case Routes.addressBookAddContact: case Routes.addressBookAddContact:
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
builder: (_) => builder: (_) =>
getIt.get<ContactPage>(param1: settings.arguments as Contact)); getIt.get<ContactPage>(param1: settings.arguments as ContactRecord));
case Routes.showKeys: case Routes.showKeys:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(

View file

@ -164,17 +164,17 @@ class ContactListPage extends BasePage {
final isDelete = final isDelete =
await showAlertDialog(context) ?? false; await showAlertDialog(context) ?? false;
if (isDelete) { // if (isDelete) {
await contactListViewModel // await contactListViewModel
.delete(contact); // .delete(contact);
} // }
}, },
), ),
], ],
dismissal: SlidableDismissal( dismissal: SlidableDismissal(
child: SlidableDrawerDismissal(), child: SlidableDrawerDismissal(),
onDismissed: (actionType) async => onDismissed: (actionType) async => null,
await contactListViewModel.delete(contact), // await contactListViewModel.delete(contact),
onWillDismiss: (actionType) async => onWillDismiss: (actionType) async =>
showAlertDialog(context), showAlertDialog(context),
), ),

View file

@ -31,7 +31,7 @@ class AccountTile extends StatelessWidget {
accountName, accountName,
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.w600,
fontFamily: 'Poppins', fontFamily: 'Poppins',
color: textColor, color: textColor,
decoration: TextDecoration.none, decoration: TextDecoration.none,

View file

@ -67,87 +67,86 @@ class NodeListPage extends BasePage {
sectionCount: 2, sectionCount: 2,
context: context, context: context,
itemBuilder: (_, sectionIndex, index) { itemBuilder: (_, sectionIndex, index) {
if (sectionIndex == 0) { return Observer(builder: (_) {
return NodeHeaderListRow( if (sectionIndex == 0) {
title: S.of(context).add_new_node, return NodeHeaderListRow(
onTap: (_) async => title: S.of(context).add_new_node,
await Navigator.of(context).pushNamed(Routes.newNode)); onTap: (_) async => await Navigator.of(context)
} .pushNamed(Routes.newNode));
}
final node = nodeListViewModel.nodes[index]; final node = nodeListViewModel.nodes[index];
final nodeListRow = NodeListRow( final isSelected = node.keyIndex ==
title: node.value.uri, nodeListViewModel.settingsStore.currentNode.keyIndex;
isSelected: node.isSelected, final nodeListRow = NodeListRow(
isAlive: node.value.requestNode(), title: node.uri,
onTap: (_) async { isSelected: isSelected,
if (node.isSelected) { isAlive: node.requestNode(),
return; onTap: (_) async {
} if (isSelected) {
return;
}
await showPopUp<void>( await showPopUp<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( // FIXME: Add translation.
content: Text( return AlertWithTwoActions(
S.of(context).change_current_node(node.value.uri), alertTitle: 'Change current node',
textAlign: TextAlign.center, alertContent:
S.of(context).change_current_node(node.uri),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () =>
Navigator.of(context).pop(),
actionRightButton: () async {
await nodeListViewModel.setAsCurrent(node);
Navigator.of(context).pop();
});
});
});
final dismissibleRow = Dismissible(
key: Key('${node.keyIndex}'),
confirmDismiss: (direction) async {
return await showPopUp(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).remove_node,
alertContent: S.of(context).remove_node_message,
rightButtonText: S.of(context).remove,
leftButtonText: S.of(context).cancel,
actionRightButton: () =>
Navigator.pop(context, true),
actionLeftButton: () =>
Navigator.pop(context, false));
});
},
onDismissed: (direction) async =>
nodeListViewModel.delete(node),
direction: DismissDirection.endToStart,
background: Container(
padding: EdgeInsets.only(right: 10.0),
alignment: AlignmentDirectional.centerEnd,
color: Palette.red,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
const Icon(
CupertinoIcons.delete,
color: Colors.white,
), ),
actions: <Widget>[ Text(
FlatButton( S.of(context).delete,
onPressed: () => Navigator.pop(context), style: TextStyle(color: Colors.white),
child: Text(S.of(context).cancel)), )
FlatButton( ],
onPressed: () async { )),
Navigator.of(context).pop(); child: nodeListRow);
await nodeListViewModel
.setAsCurrent(node.value);
},
child: Text(S.of(context).change)),
],
);
});
});
final dismissibleRow = Dismissible( return isSelected ? nodeListRow : dismissibleRow;
key: Key('${node.keyIndex}'), });
confirmDismiss: (direction) async {
return await showPopUp(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).remove_node,
alertContent: S.of(context).remove_node_message,
rightButtonText: S.of(context).remove,
leftButtonText: S.of(context).cancel,
actionRightButton: () =>
Navigator.pop(context, true),
actionLeftButton: () =>
Navigator.pop(context, false));
});
},
onDismissed: (direction) async =>
nodeListViewModel.delete(node.value),
direction: DismissDirection.endToStart,
background: Container(
padding: EdgeInsets.only(right: 10.0),
alignment: AlignmentDirectional.centerEnd,
color: Palette.red,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
const Icon(
CupertinoIcons.delete,
color: Colors.white,
),
Text(
S.of(context).delete,
style: TextStyle(color: Colors.white),
)
],
)),
child: nodeListRow);
return node.isSelected ? nodeListRow : dismissibleRow;
}, },
itemCounter: (int sectionIndex) { itemCounter: (int sectionIndex) {
if (sectionIndex == 0) { if (sectionIndex == 0) {

View file

@ -1,12 +1,12 @@
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/contact_record.dart';
part 'contact_list_store.g.dart'; part 'contact_list_store.g.dart';
class ContactListStore = ContactListStoreBase with _$ContactListStore; class ContactListStore = ContactListStoreBase with _$ContactListStore;
abstract class ContactListStoreBase with Store { abstract class ContactListStoreBase with Store {
ContactListStoreBase() : contacts = ObservableList<Contact>(); ContactListStoreBase() : contacts = ObservableList<ContactRecord>();
final ObservableList<Contact> contacts; final ObservableList<ContactRecord> contacts;
} }

View file

@ -22,17 +22,13 @@ abstract class NodeListStoreBase with Store {
final nodeSource = getIt.get<Box<Node>>(); final nodeSource = getIt.get<Box<Node>>();
_instance = NodeListStore(); _instance = NodeListStore();
_instance.replaceValues(nodeSource.values); _instance.nodes.clear();
_instance.nodes.addAll(nodeSource.values);
_onNodesSourceChange?.cancel(); _onNodesSourceChange?.cancel();
_onNodesSourceChange = bindBox(nodeSource, _instance.nodes); _onNodesSourceChange = nodeSource.bindToList(_instance.nodes);
return _instance; return _instance;
} }
final ObservableList<Node> nodes; final ObservableList<Node> nodes;
void replaceValues(Iterable<Node> newNodes) {
nodes.clear();
nodes.addAll(newNodes);
}
} }

View file

@ -21,7 +21,6 @@ class SettingsStore = SettingsStoreBase with _$SettingsStore;
abstract class SettingsStoreBase with Store { abstract class SettingsStoreBase with Store {
SettingsStoreBase( SettingsStoreBase(
{@required SharedPreferences sharedPreferences, {@required SharedPreferences sharedPreferences,
@required Box<Node> nodeSource,
@required FiatCurrency initialFiatCurrency, @required FiatCurrency initialFiatCurrency,
@required TransactionPriority initialTransactionPriority, @required TransactionPriority initialTransactionPriority,
@required BalanceDisplayMode initialBalanceDisplayMode, @required BalanceDisplayMode initialBalanceDisplayMode,
@ -43,10 +42,9 @@ abstract class SettingsStoreBase with Store {
pinCodeLength = initialPinLength; pinCodeLength = initialPinLength;
languageCode = initialLanguageCode; languageCode = initialLanguageCode;
currentLocale = initialCurrentLocale; currentLocale = initialCurrentLocale;
itemHeaders = {}; currentNode = nodes[WalletType.monero];
this.nodes = ObservableMap<WalletType, Node>.of(nodes); this.nodes = ObservableMap<WalletType, Node>.of(nodes);
_sharedPreferences = sharedPreferences; _sharedPreferences = sharedPreferences;
_nodeSource = nodeSource;
reaction( reaction(
(_) => allowBiometricalAuthentication, (_) => allowBiometricalAuthentication,
@ -58,6 +56,9 @@ abstract class SettingsStoreBase with Store {
(_) => pinCodeLength, (_) => pinCodeLength,
(int pinLength) => sharedPreferences.setInt( (int pinLength) => sharedPreferences.setInt(
PreferencesKey.currentPinLength, pinLength)); PreferencesKey.currentPinLength, pinLength));
reaction((_) => currentNode,
(Node node) => _saveCurrentNode(node, WalletType.monero));
} }
static const defaultPinLength = 4; static const defaultPinLength = 4;
@ -88,7 +89,7 @@ abstract class SettingsStoreBase with Store {
int pinCodeLength; int pinCodeLength;
@observable @observable
Map<String, String> itemHeaders; Node currentNode;
String languageCode; String languageCode;
@ -97,7 +98,6 @@ abstract class SettingsStoreBase with Store {
String appVersion; String appVersion;
SharedPreferences _sharedPreferences; SharedPreferences _sharedPreferences;
Box<Node> _nodeSource;
ObservableMap<WalletType, Node> nodes; ObservableMap<WalletType, Node> nodes;
@ -150,7 +150,6 @@ abstract class SettingsStoreBase with Store {
WalletType.monero: moneroNode, WalletType.monero: moneroNode,
WalletType.bitcoin: bitcoinElectrumServer WalletType.bitcoin: bitcoinElectrumServer
}, },
nodeSource: nodeSource,
appVersion: packageInfo.version, appVersion: packageInfo.version,
initialFiatCurrency: currentFiatCurrency, initialFiatCurrency: currentFiatCurrency,
initialTransactionPriority: currentTransactionPriority, initialTransactionPriority: currentTransactionPriority,
@ -164,7 +163,7 @@ abstract class SettingsStoreBase with Store {
initialCurrentLocale: initialCurrentLocale); initialCurrentLocale: initialCurrentLocale);
} }
Future<void> setCurrentNode(Node node, WalletType walletType) async { Future<void> _saveCurrentNode(Node node, WalletType walletType) async {
switch (walletType) { switch (walletType) {
case WalletType.bitcoin: case WalletType.bitcoin:
await _sharedPreferences.setInt( await _sharedPreferences.setInt(

View file

@ -1,11 +1,18 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/utils/mobx.dart'; import 'package:cake_wallet/utils/mobx.dart';
// part 'node_list_view_model.g.dart';
//
// class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel;
class ItemCell<Item> with Keyable { class ItemCell<Item> with Keyable {
ItemCell(this.value, {@required this.isSelected, @required dynamic key}) { ItemCell(this.value, {this.isSelectedBuilder, @required dynamic key}) {
keyIndex = key; keyIndex = key;
} }
final Item value; final Item value;
final bool isSelected;
bool get isSelected => isSelectedBuilder(value);
bool Function(Item item) isSelectedBuilder;
} }

View file

@ -6,36 +6,6 @@ mixin Keyable {
dynamic keyIndex; dynamic keyIndex;
} }
void connectWithTransform<T extends Keyable, Y extends Keyable>(
ObservableList<T> source, ObservableList<Y> dest, Y Function(T) transform,
{bool Function(T) filter}) {
source.observe((ListChange<T> change) {
change.elementChanges.forEach((change) {
switch (change.type) {
case OperationType.add:
if (filter?.call(change.newValue as T) ?? true) {
dest.add(transform(change.newValue as T));
}
break;
case OperationType.remove:
// Hive could has equal index and key
dest.removeWhere(
(elem) => elem.keyIndex == (change.oldValue.key ?? change.index));
break;
case OperationType.update:
for (var i = 0; i < dest.length; i++) {
final item = dest[i];
if (item.keyIndex == change.newValue.key) {
dest[i] = transform(change.newValue as T);
}
}
break;
}
});
});
}
void connectMapToListWithTransform<T extends Keyable, Y extends Keyable>( void connectMapToListWithTransform<T extends Keyable, Y extends Keyable>(
ObservableMap<dynamic, T> source, ObservableMap<dynamic, T> source,
ObservableList<Y> dest, ObservableList<Y> dest,
@ -50,8 +20,8 @@ void connectMapToListWithTransform<T extends Keyable, Y extends Keyable>(
break; break;
case OperationType.remove: case OperationType.remove:
// Hive could has equal index and key // Hive could has equal index and key
dest.removeWhere( dest.removeWhere((elem) =>
(elem) => elem.keyIndex == (change.key ?? change.newValue.keyIndex)); elem.keyIndex == (change.key ?? change.newValue.keyIndex));
break; break;
case OperationType.update: case OperationType.update:
for (var i = 0; i < dest.length; i++) { for (var i = 0; i < dest.length; i++) {
@ -66,54 +36,124 @@ void connectMapToListWithTransform<T extends Keyable, Y extends Keyable>(
}); });
} }
void connect<T extends Keyable>( typedef Filter<T> = bool Function(T);
ObservableList<T> source, ObservableList<T> dest) { typedef Transform<T, Y> = Y Function(T);
source.observe((ListChange<T> change) {
source.observe((ListChange<T> change) {
change.elementChanges.forEach((change) {
switch (change.type) {
case OperationType.add:
// if (filter?.call(change.newValue as T) ?? true) {
dest.add(change.newValue as T);
// }
break;
case OperationType.remove:
// Hive could has equal index and key
dest.removeWhere((elem) =>
elem.keyIndex == (change.oldValue.key ?? change.index));
break;
case OperationType.update:
for (var i = 0; i < dest.length; i++) {
final item = dest[i];
if (item.keyIndex == change.newValue.key) { enum ChangeType { update, delete, add }
dest[i] = change.newValue as T;
} class EntityChange<T extends Keyable> {
} EntityChange(this.value, this.type, {dynamic key}) : _key = key;
break;
} dynamic get key => _key ?? value.keyIndex;
}); final T value;
}); final ChangeType type;
}); final dynamic _key;
} }
StreamSubscription<BoxEvent> bindBox<T extends Keyable>( extension MobxBindable<T extends Keyable> on Box<T> {
Box<T> source, ObservableList<T> dest) { StreamSubscription<BoxEvent> bindToList(
return source.watch().listen((event) { ObservableList<T> dest, {
bool initialFire = false,
Filter<T> filter,
}) {
if (initialFire) {
dest.addAll(values);
}
return watch().listen((event) {
if (filter != null && !filter(event.value as T)) {
return;
}
dest.acceptBoxChange(event);
});
}
StreamSubscription<BoxEvent> bindToListWithTransform<Y extends Keyable>(
ObservableList<Y> dest,
Transform<T, Y> transform, {
bool initialFire = false,
Filter<T> filter,
}) {
if (initialFire) {
dest.addAll(values.map((value) => transform(value)));
}
return watch().listen((event) {
if (filter != null && !filter(event.value as T)) {
return;
}
dest.acceptBoxChange(event, transformed: transform(event.value as T));
});
}
}
extension HiveBindable<T extends Keyable> on ObservableList<T> {
Stream<EntityChange<T>> listen() {
// ignore: close_sinks
final controller = StreamController<EntityChange<T>>();
observe((ListChange<T> change) {
change.elementChanges.forEach((change) {
ChangeType type;
switch (change.type) {
case OperationType.add:
type = ChangeType.add;
break;
case OperationType.remove:
type = ChangeType.delete;
break;
case OperationType.update:
type = ChangeType.update;
break;
}
final value = change.newValue as T;
controller.add(EntityChange(value, type));
});
});
return controller.stream;
}
StreamSubscription<EntityChange<T>> bindToList(ObservableList<T> dest) =>
listen().listen((event) => dest.acceptEntityChange(event));
void acceptBoxChange(BoxEvent event, {T transformed}) {
if (event.deleted) { if (event.deleted) {
dest.removeWhere((el) => el.keyIndex == event.key); removeWhere((el) => el.keyIndex == event.key);
}
final dynamic value = transformed ?? event.value;
if (value is T) {
final index = indexWhere((el) => el.keyIndex == value.keyIndex);
if (index > -1) {
this.setAll(index, [value]); // FIXME: fixme
} else {
add(value);
}
}
}
void acceptEntityChange(EntityChange<T> event) {
if (event.type == ChangeType.delete) {
removeWhere((el) => el.keyIndex == event.key);
} }
final dynamic value = event.value; final dynamic value = event.value;
if (value is T) { if (value is T) {
final elIndex = dest.indexWhere((el) => el.keyIndex == value.keyIndex); final index = indexWhere((el) => el.keyIndex == value.keyIndex);
if (elIndex > -1) { if (index > -1) {
dest[elIndex] = value; this.setAll(index, [value]); // FIXME: fixme
} else { } else {
dest.add(value); add(value);
} }
} }
}); }
} }

View file

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/core/contact_service.dart'; import 'package:cake_wallet/core/contact_service.dart';
@ -14,19 +15,21 @@ class ContactListViewModel = ContactListViewModelBase
abstract class ContactListViewModelBase with Store { abstract class ContactListViewModelBase with Store {
ContactListViewModelBase( ContactListViewModelBase(
this.addressBookStore, this.contactService, this.contactSource) { this.addressBookStore, this.contactService, this.contactSource) {
_subscription = bindBox(contactSource, addressBookStore.contacts); _subscription = contactSource.bindToListWithTransform(addressBookStore.contacts,
(Contact contact) => ContactRecord(contactSource, contact),
initialFire: true);
} }
final ContactListStore addressBookStore; final ContactListStore addressBookStore;
final ContactService contactService; final ContactService contactService;
final Box<Contact> contactSource; final Box<Contact> contactSource;
ObservableList<Contact> get contacts => addressBookStore.contacts; ObservableList<ContactRecord> get contacts => addressBookStore.contacts;
StreamSubscription<BoxEvent> _subscription; StreamSubscription<BoxEvent> _subscription;
void dispose() { void dispose() {
_subscription.cancel(); // _subscription.cancel();
} }
Future<void> delete(Contact contact) async => contactService.delete(contact); Future<void> delete(Contact contact) async => contactService.delete(contact);

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/execution_state.dart';
@ -11,7 +12,7 @@ part 'contact_view_model.g.dart';
class ContactViewModel = ContactViewModelBase with _$ContactViewModel; class ContactViewModel = ContactViewModelBase with _$ContactViewModel;
abstract class ContactViewModelBase with Store { abstract class ContactViewModelBase with Store {
ContactViewModelBase(this._contacts, this._wallet, {Contact contact}) ContactViewModelBase(this._contacts, this._wallet, {ContactRecord contact})
: state = InitialExecutionState(), : state = InitialExecutionState(),
currencies = CryptoCurrency.all, currencies = CryptoCurrency.all,
_contact = contact { _contact = contact {
@ -41,7 +42,7 @@ abstract class ContactViewModelBase with Store {
final List<CryptoCurrency> currencies; final List<CryptoCurrency> currencies;
final WalletBase _wallet; final WalletBase _wallet;
final Box<Contact> _contacts; final Box<Contact> _contacts;
final Contact _contact; final ContactRecord _contact;
@action @action
void reset() { void reset() {
@ -57,9 +58,12 @@ abstract class ContactViewModelBase with Store {
if (_contact != null) { if (_contact != null) {
_contact.name = name; _contact.name = name;
_contact.address = address; _contact.address = address;
_contact.updateCryptoCurrency(currency: currency); _contact.type = currency;
await _contacts.put(_contact.key, _contact); await _contact.save();
// await _contacts.put(_contact.key, _contact);
} else { } else {
// final contact = ContactRecordBase.create(_contacts, name, address, currency);
// await contact.save();
await _contacts await _contacts
.add(Contact(name: name, address: address, type: currency)); .add(Contact(name: name, address: address, type: currency));
} }

View file

@ -1,45 +1,29 @@
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/node.dart';
import 'package:cake_wallet/entities/node_list.dart'; import 'package:cake_wallet/entities/node_list.dart';
import 'package:cake_wallet/store/node_list_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/entities/default_settings_migration.dart'; import 'package:cake_wallet/entities/default_settings_migration.dart';
import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/utils/mobx.dart'; import 'package:cake_wallet/utils/mobx.dart';
import 'package:cake_wallet/utils/item_cell.dart';
part 'node_list_view_model.g.dart'; part 'node_list_view_model.g.dart';
class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel; class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel;
abstract class NodeListViewModelBase with Store { abstract class NodeListViewModelBase with Store {
NodeListViewModelBase( NodeListViewModelBase(this._nodeSource, this._wallet, this.settingsStore)
this._nodeListStore, this._nodeSource, this._wallet, this._settingsStore) : nodes = ObservableList<Node>() {
: nodes = ObservableList<ItemCell<Node>>() { _nodeSource.bindToList(nodes,
final currentNode = _settingsStore.getCurrentNode(_wallet.type); filter: (Node val) => val.type == _wallet.type, initialFire: true);
final values = _nodeListStore.nodes;
nodes.clear();
nodes.addAll(values.where((Node node) => node.type == _wallet.type).map(
(Node val) => ItemCell<Node>(val,
isSelected: val.key == currentNode.key, key: val.key)));
connectWithTransform(
_nodeListStore.nodes,
nodes,
(Node val) => ItemCell<Node>(val,
isSelected: val.key == currentNode.key, key: val.key),
filter: (Node val) => val.type == _wallet.type);
reaction((_) => _settingsStore.nodes[_wallet.type],
(Node _) => _updateCurrentNode());
} }
ObservableList<ItemCell<Node>> nodes; final ObservableList<Node> nodes;
final SettingsStore settingsStore;
final WalletBase _wallet; final WalletBase _wallet;
final Box<Node> _nodeSource; final Box<Node> _nodeSource;
final NodeListStore _nodeListStore;
final SettingsStore _settingsStore;
Future<void> reset() async { Future<void> reset() async {
await resetToDefault(_nodeSource); await resetToDefault(_nodeSource);
@ -64,24 +48,6 @@ abstract class NodeListViewModelBase with Store {
Future<void> delete(Node node) async => _nodeSource.delete(node.key); Future<void> delete(Node node) async => _nodeSource.delete(node.key);
Future<void> setAsCurrent(Node node) async { Future<void> setAsCurrent(Node node) async =>
await _settingsStore.setCurrentNode(node, _wallet.type); settingsStore.currentNode = node;
_updateCurrentNode();
await _wallet.connectToNode(node: node);
}
@action
void _updateCurrentNode() {
final currentNode = _settingsStore.getCurrentNode(_wallet.type);
for (var i = 0; i < nodes.length; i++) {
final item = nodes[i];
final isSelected = item.value.key == currentNode.key;
if (item.isSelected != isSelected) {
nodes[i] = ItemCell<Node>(item.value,
isSelected: isSelected, key: item.keyIndex);
}
}
}
} }

View file

@ -217,7 +217,7 @@ packages:
name: connectivity name: connectivity
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.9+2" version: "0.4.9+3"
connectivity_for_web: connectivity_for_web:
dependency: transitive dependency: transitive
description: description:
@ -252,7 +252,7 @@ packages:
name: crypto name: crypto
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.4" version: "2.1.5"
csslib: csslib:
dependency: transitive dependency: transitive
description: description:
@ -395,7 +395,7 @@ packages:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.9" version: "1.0.11"
flutter_secure_storage: flutter_secure_storage:
dependency: "direct main" dependency: "direct main"
description: description:
@ -470,7 +470,7 @@ packages:
name: hive_generator name: hive_generator
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.1" version: "0.7.2+1"
html: html:
dependency: transitive dependency: transitive
description: description:
@ -505,7 +505,7 @@ packages:
name: image name: image
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.12" version: "2.1.18"
intl: intl:
dependency: "direct main" dependency: "direct main"
description: description:
@ -533,14 +533,14 @@ packages:
name: json_annotation name: json_annotation
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.1" version: "3.1.0"
local_auth: local_auth:
dependency: "direct main" dependency: "direct main"
description: description:
name: local_auth name: local_auth
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.3+1" version: "0.6.3+2"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -645,7 +645,7 @@ packages:
name: path_provider name: path_provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.17" version: "1.6.18"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@ -687,7 +687,7 @@ packages:
name: petitparser name: petitparser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.4.0" version: "3.0.4"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@ -695,13 +695,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.1" version: "2.2.1"
platform_detect:
dependency: transitive
description:
name: platform_detect
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -785,14 +778,14 @@ packages:
name: share name: share
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.5+1" version: "0.6.5+2"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.5.11" version: "0.5.12"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:
@ -937,7 +930,7 @@ packages:
name: url_launcher name: url_launcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.7.0" version: "5.7.2"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@ -965,7 +958,7 @@ packages:
name: url_launcher_web name: url_launcher_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.3+2" version: "0.1.4+1"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
@ -1021,7 +1014,7 @@ packages:
name: xml name: xml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.6.1" version: "4.5.1"
yaml: yaml:
dependency: "direct main" dependency: "direct main"
description: description:

View file

@ -14,7 +14,7 @@ description: Cake Wallet.
version: 1.0.5+5 version: 1.0.5+5
environment: environment:
sdk: ">=2.2.2 <3.0.0" sdk: ">=2.7.0 <3.0.0"
dependencies: dependencies:
flutter: flutter: