mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 19:49:22 +00:00
Fixes
This commit is contained in:
parent
dcdc411d41
commit
51cf11127c
21 changed files with 362 additions and 254 deletions
|
@ -22,7 +22,7 @@ class ContactService {
|
|||
if (index >= 0) {
|
||||
_forceUpdateContactListStore();
|
||||
} else {
|
||||
contactListStore.contacts.add(contact);
|
||||
// contactListStore.contacts.add(contact);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,6 @@ class ContactService {
|
|||
|
||||
void _forceUpdateContactListStore() {
|
||||
contactListStore.contacts.clear();
|
||||
contactListStore.contacts.addAll(contactSource.values);
|
||||
// contactListStore.contacts.addAll(contactSource.values);
|
||||
}
|
||||
}
|
||||
|
|
17
lib/di.dart
17
lib/di.dart
|
@ -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/wallet_service.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/entities/contact.dart';
|
||||
import 'package:cake_wallet/entities/node.dart';
|
||||
|
@ -197,7 +198,8 @@ Future setup(
|
|||
getIt
|
||||
.registerFactoryParam<AuthPage, void Function(bool, AuthPageState), bool>(
|
||||
(onAuthFinished, closable) => AuthPage(getIt.get<AuthViewModel>(),
|
||||
onAuthenticationFinished: onAuthFinished, closable: closable ?? false));
|
||||
onAuthenticationFinished: onAuthFinished,
|
||||
closable: closable ?? false));
|
||||
|
||||
getIt.registerFactory<DashboardPage>(() => DashboardPage(
|
||||
walletViewModel: getIt.get<DashboardViewModel>(),
|
||||
|
@ -282,8 +284,8 @@ Future setup(
|
|||
|
||||
getIt.registerFactory(() => WalletKeysPage(getIt.get<WalletKeysViewModel>()));
|
||||
|
||||
getIt.registerFactoryParam<ContactViewModel, Contact, void>(
|
||||
(Contact contact, _) => ContactViewModel(
|
||||
getIt.registerFactoryParam<ContactViewModel, ContactRecord, void>(
|
||||
(ContactRecord contact, _) => ContactViewModel(
|
||||
contactSource, getIt.get<AppStore>().wallet,
|
||||
contact: contact));
|
||||
|
||||
|
@ -296,13 +298,14 @@ Future setup(
|
|||
(bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(),
|
||||
isEditable: isEditable));
|
||||
|
||||
getIt.registerFactoryParam<ContactPage, Contact, void>((Contact contact, _) =>
|
||||
ContactPage(getIt.get<ContactViewModel>(param1: contact)));
|
||||
getIt.registerFactoryParam<ContactPage, ContactRecord, void>(
|
||||
(ContactRecord contact, _) =>
|
||||
ContactPage(getIt.get<ContactViewModel>(param1: contact)));
|
||||
|
||||
getIt.registerFactory(() {
|
||||
final appStore = getIt.get<AppStore>();
|
||||
return NodeListViewModel(appStore.nodeListStore, nodeSource,
|
||||
appStore.wallet, appStore.settingsStore);
|
||||
return NodeListViewModel(
|
||||
nodeSource, appStore.wallet, appStore.settingsStore);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));
|
||||
|
|
40
lib/entities/contact_record.dart
Normal file
40
lib/entities/contact_record.dart
Normal 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;
|
||||
}
|
||||
}
|
|
@ -38,6 +38,9 @@ class Node extends HiveObject with Keyable {
|
|||
@HiveField(3)
|
||||
int typeRaw;
|
||||
|
||||
@override
|
||||
dynamic get keyIndex => key;
|
||||
|
||||
WalletType get type => deserializeFromInt(typeRaw);
|
||||
|
||||
set type(WalletType type) => typeRaw = serializeToInt(type);
|
||||
|
|
35
lib/entities/record.dart
Normal file
35
lib/entities/record.dart
Normal 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();
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:cake_wallet/reactions/on_current_node_change.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
@ -31,4 +32,5 @@ Future<void> bootstrap(GlobalKey<NavigatorState> navigatorKey) async {
|
|||
startCurrentWalletChangeReaction(
|
||||
appStore, settingsStore, fiatConversionStore);
|
||||
startCurrentFiatChangeReaction(appStore, settingsStore);
|
||||
startOnCurrentNodeChangeReaction(appStore);
|
||||
}
|
||||
|
|
17
lib/reactions/on_current_node_change.dart
Normal file
17
lib/reactions/on_current_node_change.dart
Normal 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());
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
|
@ -252,7 +253,7 @@ class Router {
|
|||
case Routes.addressBookAddContact:
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) =>
|
||||
getIt.get<ContactPage>(param1: settings.arguments as Contact));
|
||||
getIt.get<ContactPage>(param1: settings.arguments as ContactRecord));
|
||||
|
||||
case Routes.showKeys:
|
||||
return MaterialPageRoute<void>(
|
||||
|
|
|
@ -164,17 +164,17 @@ class ContactListPage extends BasePage {
|
|||
final isDelete =
|
||||
await showAlertDialog(context) ?? false;
|
||||
|
||||
if (isDelete) {
|
||||
await contactListViewModel
|
||||
.delete(contact);
|
||||
}
|
||||
// if (isDelete) {
|
||||
// await contactListViewModel
|
||||
// .delete(contact);
|
||||
// }
|
||||
},
|
||||
),
|
||||
],
|
||||
dismissal: SlidableDismissal(
|
||||
child: SlidableDrawerDismissal(),
|
||||
onDismissed: (actionType) async =>
|
||||
await contactListViewModel.delete(contact),
|
||||
onDismissed: (actionType) async => null,
|
||||
// await contactListViewModel.delete(contact),
|
||||
onWillDismiss: (actionType) async =>
|
||||
showAlertDialog(context),
|
||||
),
|
||||
|
|
|
@ -31,7 +31,7 @@ class AccountTile extends StatelessWidget {
|
|||
accountName,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'Poppins',
|
||||
color: textColor,
|
||||
decoration: TextDecoration.none,
|
||||
|
|
|
@ -67,87 +67,86 @@ class NodeListPage extends BasePage {
|
|||
sectionCount: 2,
|
||||
context: context,
|
||||
itemBuilder: (_, sectionIndex, index) {
|
||||
if (sectionIndex == 0) {
|
||||
return NodeHeaderListRow(
|
||||
title: S.of(context).add_new_node,
|
||||
onTap: (_) async =>
|
||||
await Navigator.of(context).pushNamed(Routes.newNode));
|
||||
}
|
||||
return Observer(builder: (_) {
|
||||
if (sectionIndex == 0) {
|
||||
return NodeHeaderListRow(
|
||||
title: S.of(context).add_new_node,
|
||||
onTap: (_) async => await Navigator.of(context)
|
||||
.pushNamed(Routes.newNode));
|
||||
}
|
||||
|
||||
final node = nodeListViewModel.nodes[index];
|
||||
final nodeListRow = NodeListRow(
|
||||
title: node.value.uri,
|
||||
isSelected: node.isSelected,
|
||||
isAlive: node.value.requestNode(),
|
||||
onTap: (_) async {
|
||||
if (node.isSelected) {
|
||||
return;
|
||||
}
|
||||
final node = nodeListViewModel.nodes[index];
|
||||
final isSelected = node.keyIndex ==
|
||||
nodeListViewModel.settingsStore.currentNode.keyIndex;
|
||||
final nodeListRow = NodeListRow(
|
||||
title: node.uri,
|
||||
isSelected: isSelected,
|
||||
isAlive: node.requestNode(),
|
||||
onTap: (_) async {
|
||||
if (isSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
content: Text(
|
||||
S.of(context).change_current_node(node.value.uri),
|
||||
textAlign: TextAlign.center,
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
// FIXME: Add translation.
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: 'Change current node',
|
||||
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>[
|
||||
FlatButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(S.of(context).cancel)),
|
||||
FlatButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
await nodeListViewModel
|
||||
.setAsCurrent(node.value);
|
||||
},
|
||||
child: Text(S.of(context).change)),
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
Text(
|
||||
S.of(context).delete,
|
||||
style: TextStyle(color: Colors.white),
|
||||
)
|
||||
],
|
||||
)),
|
||||
child: nodeListRow);
|
||||
|
||||
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.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;
|
||||
return isSelected ? nodeListRow : dismissibleRow;
|
||||
});
|
||||
},
|
||||
itemCounter: (int sectionIndex) {
|
||||
if (sectionIndex == 0) {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
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';
|
||||
|
||||
class ContactListStore = ContactListStoreBase with _$ContactListStore;
|
||||
|
||||
abstract class ContactListStoreBase with Store {
|
||||
ContactListStoreBase() : contacts = ObservableList<Contact>();
|
||||
ContactListStoreBase() : contacts = ObservableList<ContactRecord>();
|
||||
|
||||
final ObservableList<Contact> contacts;
|
||||
final ObservableList<ContactRecord> contacts;
|
||||
}
|
||||
|
|
|
@ -22,17 +22,13 @@ abstract class NodeListStoreBase with Store {
|
|||
|
||||
final nodeSource = getIt.get<Box<Node>>();
|
||||
_instance = NodeListStore();
|
||||
_instance.replaceValues(nodeSource.values);
|
||||
_instance.nodes.clear();
|
||||
_instance.nodes.addAll(nodeSource.values);
|
||||
_onNodesSourceChange?.cancel();
|
||||
_onNodesSourceChange = bindBox(nodeSource, _instance.nodes);
|
||||
_onNodesSourceChange = nodeSource.bindToList(_instance.nodes);
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
final ObservableList<Node> nodes;
|
||||
|
||||
void replaceValues(Iterable<Node> newNodes) {
|
||||
nodes.clear();
|
||||
nodes.addAll(newNodes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ class SettingsStore = SettingsStoreBase with _$SettingsStore;
|
|||
abstract class SettingsStoreBase with Store {
|
||||
SettingsStoreBase(
|
||||
{@required SharedPreferences sharedPreferences,
|
||||
@required Box<Node> nodeSource,
|
||||
@required FiatCurrency initialFiatCurrency,
|
||||
@required TransactionPriority initialTransactionPriority,
|
||||
@required BalanceDisplayMode initialBalanceDisplayMode,
|
||||
|
@ -43,10 +42,9 @@ abstract class SettingsStoreBase with Store {
|
|||
pinCodeLength = initialPinLength;
|
||||
languageCode = initialLanguageCode;
|
||||
currentLocale = initialCurrentLocale;
|
||||
itemHeaders = {};
|
||||
currentNode = nodes[WalletType.monero];
|
||||
this.nodes = ObservableMap<WalletType, Node>.of(nodes);
|
||||
_sharedPreferences = sharedPreferences;
|
||||
_nodeSource = nodeSource;
|
||||
|
||||
reaction(
|
||||
(_) => allowBiometricalAuthentication,
|
||||
|
@ -58,6 +56,9 @@ abstract class SettingsStoreBase with Store {
|
|||
(_) => pinCodeLength,
|
||||
(int pinLength) => sharedPreferences.setInt(
|
||||
PreferencesKey.currentPinLength, pinLength));
|
||||
|
||||
reaction((_) => currentNode,
|
||||
(Node node) => _saveCurrentNode(node, WalletType.monero));
|
||||
}
|
||||
|
||||
static const defaultPinLength = 4;
|
||||
|
@ -88,7 +89,7 @@ abstract class SettingsStoreBase with Store {
|
|||
int pinCodeLength;
|
||||
|
||||
@observable
|
||||
Map<String, String> itemHeaders;
|
||||
Node currentNode;
|
||||
|
||||
String languageCode;
|
||||
|
||||
|
@ -97,7 +98,6 @@ abstract class SettingsStoreBase with Store {
|
|||
String appVersion;
|
||||
|
||||
SharedPreferences _sharedPreferences;
|
||||
Box<Node> _nodeSource;
|
||||
|
||||
ObservableMap<WalletType, Node> nodes;
|
||||
|
||||
|
@ -150,7 +150,6 @@ abstract class SettingsStoreBase with Store {
|
|||
WalletType.monero: moneroNode,
|
||||
WalletType.bitcoin: bitcoinElectrumServer
|
||||
},
|
||||
nodeSource: nodeSource,
|
||||
appVersion: packageInfo.version,
|
||||
initialFiatCurrency: currentFiatCurrency,
|
||||
initialTransactionPriority: currentTransactionPriority,
|
||||
|
@ -164,7 +163,7 @@ abstract class SettingsStoreBase with Store {
|
|||
initialCurrentLocale: initialCurrentLocale);
|
||||
}
|
||||
|
||||
Future<void> setCurrentNode(Node node, WalletType walletType) async {
|
||||
Future<void> _saveCurrentNode(Node node, WalletType walletType) async {
|
||||
switch (walletType) {
|
||||
case WalletType.bitcoin:
|
||||
await _sharedPreferences.setInt(
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/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 {
|
||||
ItemCell(this.value, {@required this.isSelected, @required dynamic key}) {
|
||||
ItemCell(this.value, {this.isSelectedBuilder, @required dynamic key}) {
|
||||
keyIndex = key;
|
||||
}
|
||||
|
||||
final Item value;
|
||||
final bool isSelected;
|
||||
|
||||
bool get isSelected => isSelectedBuilder(value);
|
||||
bool Function(Item item) isSelectedBuilder;
|
||||
}
|
||||
|
|
|
@ -6,36 +6,6 @@ mixin Keyable {
|
|||
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>(
|
||||
ObservableMap<dynamic, T> source,
|
||||
ObservableList<Y> dest,
|
||||
|
@ -50,8 +20,8 @@ void connectMapToListWithTransform<T extends Keyable, Y extends Keyable>(
|
|||
break;
|
||||
case OperationType.remove:
|
||||
// Hive could has equal index and key
|
||||
dest.removeWhere(
|
||||
(elem) => elem.keyIndex == (change.key ?? change.newValue.keyIndex));
|
||||
dest.removeWhere((elem) =>
|
||||
elem.keyIndex == (change.key ?? change.newValue.keyIndex));
|
||||
break;
|
||||
case OperationType.update:
|
||||
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>(
|
||||
ObservableList<T> source, ObservableList<T> dest) {
|
||||
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];
|
||||
typedef Filter<T> = bool Function(T);
|
||||
typedef Transform<T, Y> = Y Function(T);
|
||||
|
||||
if (item.keyIndex == change.newValue.key) {
|
||||
dest[i] = change.newValue as T;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
enum ChangeType { update, delete, add }
|
||||
|
||||
class EntityChange<T extends Keyable> {
|
||||
EntityChange(this.value, this.type, {dynamic key}) : _key = key;
|
||||
|
||||
dynamic get key => _key ?? value.keyIndex;
|
||||
final T value;
|
||||
final ChangeType type;
|
||||
final dynamic _key;
|
||||
}
|
||||
|
||||
StreamSubscription<BoxEvent> bindBox<T extends Keyable>(
|
||||
Box<T> source, ObservableList<T> dest) {
|
||||
return source.watch().listen((event) {
|
||||
extension MobxBindable<T extends Keyable> on Box<T> {
|
||||
StreamSubscription<BoxEvent> bindToList(
|
||||
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) {
|
||||
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;
|
||||
|
||||
if (value is T) {
|
||||
final elIndex = dest.indexWhere((el) => el.keyIndex == value.keyIndex);
|
||||
final index = indexWhere((el) => el.keyIndex == value.keyIndex);
|
||||
|
||||
if (elIndex > -1) {
|
||||
dest[elIndex] = value;
|
||||
if (index > -1) {
|
||||
this.setAll(index, [value]); // FIXME: fixme
|
||||
} else {
|
||||
dest.add(value);
|
||||
add(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/core/contact_service.dart';
|
||||
|
@ -14,19 +15,21 @@ class ContactListViewModel = ContactListViewModelBase
|
|||
abstract class ContactListViewModelBase with Store {
|
||||
ContactListViewModelBase(
|
||||
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 ContactService contactService;
|
||||
final Box<Contact> contactSource;
|
||||
|
||||
ObservableList<Contact> get contacts => addressBookStore.contacts;
|
||||
ObservableList<ContactRecord> get contacts => addressBookStore.contacts;
|
||||
|
||||
StreamSubscription<BoxEvent> _subscription;
|
||||
|
||||
void dispose() {
|
||||
_subscription.cancel();
|
||||
// _subscription.cancel();
|
||||
}
|
||||
|
||||
Future<void> delete(Contact contact) async => contactService.delete(contact);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
|
@ -11,7 +12,7 @@ part 'contact_view_model.g.dart';
|
|||
class ContactViewModel = ContactViewModelBase with _$ContactViewModel;
|
||||
|
||||
abstract class ContactViewModelBase with Store {
|
||||
ContactViewModelBase(this._contacts, this._wallet, {Contact contact})
|
||||
ContactViewModelBase(this._contacts, this._wallet, {ContactRecord contact})
|
||||
: state = InitialExecutionState(),
|
||||
currencies = CryptoCurrency.all,
|
||||
_contact = contact {
|
||||
|
@ -41,7 +42,7 @@ abstract class ContactViewModelBase with Store {
|
|||
final List<CryptoCurrency> currencies;
|
||||
final WalletBase _wallet;
|
||||
final Box<Contact> _contacts;
|
||||
final Contact _contact;
|
||||
final ContactRecord _contact;
|
||||
|
||||
@action
|
||||
void reset() {
|
||||
|
@ -57,9 +58,12 @@ abstract class ContactViewModelBase with Store {
|
|||
if (_contact != null) {
|
||||
_contact.name = name;
|
||||
_contact.address = address;
|
||||
_contact.updateCryptoCurrency(currency: currency);
|
||||
await _contacts.put(_contact.key, _contact);
|
||||
_contact.type = currency;
|
||||
await _contact.save();
|
||||
// await _contacts.put(_contact.key, _contact);
|
||||
} else {
|
||||
// final contact = ContactRecordBase.create(_contacts, name, address, currency);
|
||||
// await contact.save();
|
||||
await _contacts
|
||||
.add(Contact(name: name, address: address, type: currency));
|
||||
}
|
||||
|
|
|
@ -1,45 +1,29 @@
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.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_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/wallet_type.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:cake_wallet/utils/item_cell.dart';
|
||||
|
||||
part 'node_list_view_model.g.dart';
|
||||
|
||||
class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel;
|
||||
|
||||
abstract class NodeListViewModelBase with Store {
|
||||
NodeListViewModelBase(
|
||||
this._nodeListStore, this._nodeSource, this._wallet, this._settingsStore)
|
||||
: nodes = ObservableList<ItemCell<Node>>() {
|
||||
final currentNode = _settingsStore.getCurrentNode(_wallet.type);
|
||||
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());
|
||||
NodeListViewModelBase(this._nodeSource, this._wallet, this.settingsStore)
|
||||
: nodes = ObservableList<Node>() {
|
||||
_nodeSource.bindToList(nodes,
|
||||
filter: (Node val) => val.type == _wallet.type, initialFire: true);
|
||||
}
|
||||
|
||||
ObservableList<ItemCell<Node>> nodes;
|
||||
final ObservableList<Node> nodes;
|
||||
final SettingsStore settingsStore;
|
||||
|
||||
final WalletBase _wallet;
|
||||
final Box<Node> _nodeSource;
|
||||
final NodeListStore _nodeListStore;
|
||||
final SettingsStore _settingsStore;
|
||||
|
||||
Future<void> reset() async {
|
||||
await resetToDefault(_nodeSource);
|
||||
|
@ -64,24 +48,6 @@ abstract class NodeListViewModelBase with Store {
|
|||
|
||||
Future<void> delete(Node node) async => _nodeSource.delete(node.key);
|
||||
|
||||
Future<void> setAsCurrent(Node node) async {
|
||||
await _settingsStore.setCurrentNode(node, _wallet.type);
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Future<void> setAsCurrent(Node node) async =>
|
||||
settingsStore.currentNode = node;
|
||||
}
|
||||
|
|
35
pubspec.lock
35
pubspec.lock
|
@ -217,7 +217,7 @@ packages:
|
|||
name: connectivity
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.9+2"
|
||||
version: "0.4.9+3"
|
||||
connectivity_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -252,7 +252,7 @@ packages:
|
|||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.5"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -395,7 +395,7 @@ packages:
|
|||
name: flutter_plugin_android_lifecycle
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.9"
|
||||
version: "1.0.11"
|
||||
flutter_secure_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -470,7 +470,7 @@ packages:
|
|||
name: hive_generator
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
version: "0.7.2+1"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -505,7 +505,7 @@ packages:
|
|||
name: image
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.12"
|
||||
version: "2.1.18"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -533,14 +533,14 @@ packages:
|
|||
name: json_annotation
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.1.0"
|
||||
local_auth:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: local_auth
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.3+1"
|
||||
version: "0.6.3+2"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -645,7 +645,7 @@ packages:
|
|||
name: path_provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.17"
|
||||
version: "1.6.18"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -687,7 +687,7 @@ packages:
|
|||
name: petitparser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
version: "3.0.4"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -695,13 +695,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -785,14 +778,14 @@ packages:
|
|||
name: share
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.5+1"
|
||||
version: "0.6.5+2"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.11"
|
||||
version: "0.5.12"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -937,7 +930,7 @@ packages:
|
|||
name: url_launcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.7.0"
|
||||
version: "5.7.2"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -965,7 +958,7 @@ packages:
|
|||
name: url_launcher_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.3+2"
|
||||
version: "0.1.4+1"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1021,7 +1014,7 @@ packages:
|
|||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.6.1"
|
||||
version: "4.5.1"
|
||||
yaml:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -14,7 +14,7 @@ description: Cake Wallet.
|
|||
version: 1.0.5+5
|
||||
|
||||
environment:
|
||||
sdk: ">=2.2.2 <3.0.0"
|
||||
sdk: ">=2.7.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
|
Loading…
Reference in a new issue