This commit is contained in:
M 2020-08-27 19:54:34 +03:00
parent 5eefd6a31b
commit 6aaac93fa8
28 changed files with 364 additions and 143 deletions

View file

@ -1,2 +1,4 @@
- -
uri: electrum2.hodlister.co:50002 uri: electrum2.hodlister.co:50002
-
uri: bitcoin.electrumx.multicoin.co:50002

View file

@ -61,7 +61,7 @@ DEPENDENCIES:
- cw_monero (from `.symlinks/plugins/cw_monero/ios`) - cw_monero (from `.symlinks/plugins/cw_monero/ios`)
- devicelocale (from `.symlinks/plugins/devicelocale/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`)
- esys_flutter_share (from `.symlinks/plugins/esys_flutter_share/ios`) - esys_flutter_share (from `.symlinks/plugins/esys_flutter_share/ios`)
- Flutter (from `.symlinks/flutter/ios`) - Flutter (from `.symlinks/flutter/ios-release`)
- flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`) - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- local_auth (from `.symlinks/plugins/local_auth/ios`) - local_auth (from `.symlinks/plugins/local_auth/ios`)
@ -92,7 +92,7 @@ EXTERNAL SOURCES:
esys_flutter_share: esys_flutter_share:
:path: ".symlinks/plugins/esys_flutter_share/ios" :path: ".symlinks/plugins/esys_flutter_share/ios"
Flutter: Flutter:
:path: ".symlinks/flutter/ios" :path: ".symlinks/flutter/ios-release"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
:path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios" :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios"
flutter_secure_storage: flutter_secure_storage:

View file

@ -373,7 +373,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 6; CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -509,7 +509,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 6; CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -540,7 +540,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 6; CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (

View file

@ -22,6 +22,8 @@
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSCameraUsageDescription</key>
<string>Cake Wallet requires access to your phones camera.</string>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>

View file

@ -200,7 +200,8 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
Future<void> startSync() async { Future<void> startSync() async {
try { try {
syncStatus = StartingSyncStatus(); syncStatus = StartingSyncStatus();
transactionHistory.updateAsync(onFinished: () => print('finished!')); transactionHistory.updateAsync(
onFinished: () => print('transactionHistory update finished!'));
_subscribeForUpdates(); _subscribeForUpdates();
await _updateBalance(); await _updateBalance();
syncStatus = SyncedSyncStatus(); syncStatus = SyncedSyncStatus();
@ -215,11 +216,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
Future<void> connectToNode({@required Node node}) async { Future<void> connectToNode({@required Node node}) async {
try { try {
syncStatus = ConnectingSyncStatus(); syncStatus = ConnectingSyncStatus();
// electrum2.hodlister.co await eclient.connectToUri(node.uri);
// bitcoin.electrumx.multicoin.co:50002
// electrum2.taborsky.cz:5002
await eclient.connect(
host: 'bitcoin.electrumx.multicoin.co', port: 50002);
syncStatus = ConnectedSyncStatus(); syncStatus = ConnectedSyncStatus();
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());

View file

@ -49,10 +49,9 @@ class BitcoinWalletService extends WalletService<
} }
@override @override
Future<void> remove(String wallet) { Future<void> remove(String wallet) async =>
// TODO: implement remove File(await pathForWalletDir(name: wallet, type: WalletType.bitcoin))
throw UnimplementedError(); .delete(recursive: true);
}
@override @override
Future<BitcoinWallet> restoreFromKeys( Future<BitcoinWallet> restoreFromKeys(

View file

@ -37,11 +37,15 @@ class ElectrumClient {
bool _isConnected; bool _isConnected;
Timer _aliveTimer; Timer _aliveTimer;
Future<void> connect({@required String host, @required int port}) async { Future<void> connectToUri(String uri) async {
if (socket != null) { final _uri = Uri.parse(uri);
await socket.close(); final host = _uri.scheme;
final port = int.parse(_uri.path);
await connect(host: host, port: port);
} }
Future<void> connect({@required String host, @required int port}) async {
await socket?.close();
final start = DateTime.now(); final start = DateTime.now();
socket = await SecureSocket.connect(host, port, timeout: connectionTimeout); socket = await SecureSocket.connect(host, port, timeout: connectionTimeout);

View file

@ -25,6 +25,7 @@ import 'package:cake_wallet/src/screens/send/send_page.dart';
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart'; import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/store/wallet_list_store.dart'; import 'package:cake_wallet/store/wallet_list_store.dart';
import 'package:cake_wallet/utils/mobx.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart'; import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart'; import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
@ -74,8 +75,20 @@ NodeListStore setupNodeListStore(Box<Node> nodeSource) {
_nodeListStore = NodeListStore(); _nodeListStore = NodeListStore();
_nodeListStore.replaceValues(nodeSource.values); _nodeListStore.replaceValues(nodeSource.values);
_onNodesSourceChange = nodeSource.watch(); _onNodesSourceChange = nodeSource.watch();
_onNodesSourceChange _onNodesSourceChange.listen((event) {
.listen((_) => _nodeListStore.replaceValues(nodeSource.values)); // print(event);
if (event.deleted) {
_nodeListStore.nodes.removeWhere((n) {
return n.key != null ? n.key == event.key : true;
});
}
if (event.value is Node) {
final val = event.value as Node;
_nodeListStore.nodes.add(val);
}
});
return _nodeListStore; return _nodeListStore;
} }
@ -274,10 +287,11 @@ Future setup(
getIt.registerFactoryParam<ContactPage, Contact, void>((Contact contact, _) => getIt.registerFactoryParam<ContactPage, Contact, void>((Contact contact, _) =>
ContactPage(getIt.get<ContactViewModel>(param1: contact))); ContactPage(getIt.get<ContactViewModel>(param1: contact)));
getIt.registerFactory(() => NodeListViewModel( getIt.registerFactory(() {
getIt.get<AppStore>().nodeListStore, final appStore = getIt.get<AppStore>();
nodeSource, return NodeListViewModel(appStore.nodeListStore, nodeSource,
getIt.get<AppStore>().wallet)); appStore.wallet, appStore.settingsStore);
});
getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>())); getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));

View file

@ -127,7 +127,7 @@ void main() async {
contactSource: contacts, contactSource: contacts,
tradesSource: trades, tradesSource: trades,
fiatConvertationService: fiatConvertationService, fiatConvertationService: fiatConvertationService,
initialMigrationVersion: 3); initialMigrationVersion: 4);
setReactions( setReactions(
settingsStore: settingsStore, settingsStore: settingsStore,
@ -169,7 +169,7 @@ Future<void> initialSetup(
@required Box<Contact> contactSource, @required Box<Contact> contactSource,
@required Box<Trade> tradesSource, @required Box<Trade> tradesSource,
@required FiatConvertationService fiatConvertationService, @required FiatConvertationService fiatConvertationService,
int initialMigrationVersion = 3}) async { int initialMigrationVersion = 4}) async {
await defaultSettingsMigration( await defaultSettingsMigration(
version: initialMigrationVersion, version: initialMigrationVersion,
sharedPreferences: sharedPreferences, sharedPreferences: sharedPreferences,

View file

@ -50,7 +50,8 @@ ReactionDisposer _onCurrentWalletChangeReaction;
ReactionDisposer _onWalletSyncStatusChangeReaction; ReactionDisposer _onWalletSyncStatusChangeReaction;
ReactionDisposer _onCurrentFiatCurrencyChangeDisposer; ReactionDisposer _onCurrentFiatCurrencyChangeDisposer;
Future<void> bootstrap({FiatConvertationService fiatConvertationService}) async { Future<void> bootstrap(
{FiatConvertationService fiatConvertationService}) async {
final authenticationStore = getIt.get<AuthenticationStore>(); final authenticationStore = getIt.get<AuthenticationStore>();
final settingsStore = getIt.get<SettingsStore>(); final settingsStore = getIt.get<SettingsStore>();
final fiatConvertationStore = getIt.get<FiatConvertationStore>(); final fiatConvertationStore = getIt.get<FiatConvertationStore>();
@ -72,12 +73,10 @@ Future<void> bootstrap({FiatConvertationService fiatConvertationService}) async
_onCurrentWalletChangeReaction ??= _onCurrentWalletChangeReaction ??=
reaction((_) => getIt.get<AppStore>().wallet, (WalletBase wallet) async { reaction((_) => getIt.get<AppStore>().wallet, (WalletBase wallet) async {
print('Wallet name ${wallet.name}');
_onWalletSyncStatusChangeReaction?.reaction?.dispose(); _onWalletSyncStatusChangeReaction?.reaction?.dispose();
_onWalletSyncStatusChangeReaction = when( _onWalletSyncStatusChangeReaction = reaction(
(_) => wallet.syncStatus is ConnectedSyncStatus, (_) => wallet.syncStatus is ConnectedSyncStatus,
() async => await wallet.startSync()); (_) async => await wallet.startSync());
await getIt await getIt
.get<SharedPreferences>() .get<SharedPreferences>()
@ -87,30 +86,24 @@ Future<void> bootstrap({FiatConvertationService fiatConvertationService}) async
.get<SharedPreferences>() .get<SharedPreferences>()
.setInt('current_wallet_type', serializeToInt(wallet.type)); .setInt('current_wallet_type', serializeToInt(wallet.type));
await wallet.connectToNode(node: null); final node = settingsStore.getCurrentNode(wallet.type);
final cryptoCurrency = wallet.currency; final cryptoCurrency = wallet.currency;
final fiatCurrency = settingsStore.fiatCurrency; final fiatCurrency = settingsStore.fiatCurrency;
await wallet.connectToNode(node: node);
final price = await fiatConvertationService.getPrice( final price = await fiatConvertationService.getPrice(
crypto: cryptoCurrency, crypto: cryptoCurrency, fiat: fiatCurrency);
fiat: fiatCurrency
);
fiatConvertationStore.setPrice(price); fiatConvertationStore.setPrice(price);
}); });
// _onCurrentFiatCurrencyChangeDisposer ??= reaction(
(_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async {
_onCurrentFiatCurrencyChangeDisposer ??=
reaction((_) => settingsStore.fiatCurrency,
(FiatCurrency fiatCurrency) async {
final cryptoCurrency = getIt.get<AppStore>().wallet.currency; final cryptoCurrency = getIt.get<AppStore>().wallet.currency;
final price = await fiatConvertationService.getPrice( final price = await fiatConvertationService.getPrice(
crypto: cryptoCurrency, crypto: cryptoCurrency, fiat: fiatCurrency);
fiat: fiatCurrency
);
fiatConvertationStore.setPrice(price); fiatConvertationStore.setPrice(price);
}); });

View file

@ -29,15 +29,19 @@ Future defaultSettingsMigration(
switch (version) { switch (version) {
case 1: case 1:
await sharedPreferences.setString( await sharedPreferences.setString(
SettingsStoreBase.currentFiatCurrencyKey, FiatCurrency.usd.toString()); SettingsStoreBase.currentFiatCurrencyKey,
FiatCurrency.usd.toString());
await sharedPreferences.setInt( await sharedPreferences.setInt(
SettingsStoreBase.currentTransactionPriorityKey, TransactionPriority.standart.raw); SettingsStoreBase.currentTransactionPriorityKey,
TransactionPriority.standart.raw);
await sharedPreferences.setInt( await sharedPreferences.setInt(
SettingsStoreBase.currentBalanceDisplayModeKey, SettingsStoreBase.currentBalanceDisplayModeKey,
BalanceDisplayMode.availableBalance.raw); BalanceDisplayMode.availableBalance.raw);
await sharedPreferences.setBool('save_recipient_address', true); await sharedPreferences.setBool('save_recipient_address', true);
await resetToDefault(nodes); await resetToDefault(nodes);
await changeCurrentNodeToDefault( await changeMoneroCurrentNodeToDefault(
sharedPreferences: sharedPreferences, nodes: nodes);
await changeBitcoinCurrentElectrumServerToDefault(
sharedPreferences: sharedPreferences, nodes: nodes); sharedPreferences: sharedPreferences, nodes: nodes);
break; break;
@ -50,6 +54,11 @@ Future defaultSettingsMigration(
case 3: case 3:
await updateNodeTypes(nodes: nodes); await updateNodeTypes(nodes: nodes);
await addBitcoinElectrumServerList(nodes: nodes); await addBitcoinElectrumServerList(nodes: nodes);
break;
case 4:
await changeBitcoinCurrentElectrumServerToDefault(
sharedPreferences: sharedPreferences, nodes: nodes);
break; break;
default: default:
break; break;
@ -69,10 +78,11 @@ Future defaultSettingsMigration(
Future<void> replaceNodesMigration({@required Box<Node> nodes}) async { Future<void> replaceNodesMigration({@required Box<Node> nodes}) async {
final replaceNodes = <String, Node>{ final replaceNodes = <String, Node>{
'eu-node.cakewallet.io:18081': 'eu-node.cakewallet.io:18081':
Node(uri: 'xmr-node-eu.cakewallet.com:18081'), Node(uri: 'xmr-node-eu.cakewallet.com:18081', type: WalletType.monero),
'node.cakewallet.io:18081': 'node.cakewallet.io:18081': Node(
Node(uri: 'xmr-node-usa-east.cakewallet.com:18081'), uri: 'xmr-node-usa-east.cakewallet.com:18081', type: WalletType.monero),
'node.xmr.ru:13666': Node(uri: 'node.monero.net:18081') 'node.xmr.ru:13666':
Node(uri: 'node.monero.net:18081', type: WalletType.monero)
}; };
nodes.values.forEach((Node node) async { nodes.values.forEach((Node node) async {
@ -87,11 +97,27 @@ Future<void> replaceNodesMigration({@required Box<Node> nodes}) async {
}); });
} }
Future<void> changeCurrentNodeToDefault( Future<void> changeMoneroCurrentNodeToDefault(
{@required SharedPreferences sharedPreferences, {@required SharedPreferences sharedPreferences,
@required Box<Node> nodes}) async { @required Box<Node> nodes}) async {
final node = getMoneroDefaultNode(nodes: nodes);
final nodeId = node?.key as int ?? 0; // 0 - England
await sharedPreferences.setInt('current_node_id', nodeId);
}
Node getBitcoinDefaultElectrumServer({@required Box<Node> nodes}) {
final uri = 'bitcoin.electrumx.multicoin.co:50002';
return nodes.values
.firstWhere((Node node) => node.uri == uri, orElse: () => null) ??
nodes.values.firstWhere((node) => node.type == WalletType.bitcoin,
orElse: () => null);
}
Node getMoneroDefaultNode({@required Box<Node> nodes}) {
final timeZone = DateTime.now().timeZoneOffset.inHours; final timeZone = DateTime.now().timeZoneOffset.inHours;
String nodeUri = ''; var nodeUri = '';
if (timeZone >= 1) { if (timeZone >= 1) {
// Eurasia // Eurasia
@ -101,11 +127,18 @@ Future<void> changeCurrentNodeToDefault(
nodeUri = 'xmr-node-usa-east.cakewallet.com:18081'; nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
} }
final node = nodes.values.firstWhere((Node node) => node.uri == nodeUri) ?? return nodes.values
.firstWhere((Node node) => node.uri == nodeUri, orElse: () => null) ??
nodes.values.first; nodes.values.first;
final nodeId = node != null ? node.key as int : 0; // 0 - England }
await sharedPreferences.setInt('current_node_id', nodeId); Future<void> changeBitcoinCurrentElectrumServerToDefault(
{@required SharedPreferences sharedPreferences,
@required Box<Node> nodes}) async {
final server = getBitcoinDefaultElectrumServer(nodes: nodes);
final serverId = server?.key as int ?? 0;
await sharedPreferences.setInt('current_node_id_btc', serverId);
} }
Future<void> replaceDefaultNode( Future<void> replaceDefaultNode(
@ -126,7 +159,7 @@ Future<void> replaceDefaultNode(
return; return;
} }
await changeCurrentNodeToDefault( await changeMoneroCurrentNodeToDefault(
sharedPreferences: sharedPreferences, nodes: nodes); sharedPreferences: sharedPreferences, nodes: nodes);
} }

View file

@ -9,12 +9,16 @@ part 'node.g.dart';
@HiveType(typeId: 1) @HiveType(typeId: 1)
class Node extends HiveObject { class Node extends HiveObject {
Node({@required this.uri, @required WalletType type, this.login, this.password}) { Node(
{@required this.uri,
@required WalletType type,
this.login,
this.password}) {
this.type = type; this.type = type;
} }
Node.fromMap(Map map) Node.fromMap(Map map)
: uri = (map['uri'] ?? '') as String, : uri = map['uri'] as String ?? '',
login = map['login'] as String, login = map['login'] as String,
password = map['password'] as String, password = map['password'] as String,
typeRaw = map['typeRaw'] as int; typeRaw = map['typeRaw'] as int;

View file

@ -10,7 +10,10 @@ Future<List<Node>> loadDefaultNodes() async {
return nodes.map((dynamic raw) { return nodes.map((dynamic raw) {
if (raw is Map) { if (raw is Map) {
return Node.fromMap(raw); final node = Node.fromMap(raw);
node?.type = WalletType.monero;
return node;
} }
return null; return null;
@ -38,13 +41,7 @@ Future resetToDefault(Box<Node> nodeSource) async {
final moneroNodes = await loadDefaultNodes(); final moneroNodes = await loadDefaultNodes();
final bitcoinElectrumServerList = await loadElectrumServerList(); final bitcoinElectrumServerList = await loadElectrumServerList();
final nodes = moneroNodes + bitcoinElectrumServerList; final nodes = moneroNodes + bitcoinElectrumServerList;
final entities = <int, Node>{};
await nodeSource.clear(); await nodeSource.clear();
await nodeSource.addAll(nodes);
for (var i = 0; i < nodes.length; i++) {
entities[i] = nodes[i];
}
await nodeSource.putAll(entities);
} }

View file

@ -50,10 +50,7 @@ class DashboardPage extends BasePage {
padding: EdgeInsets.all(0), padding: EdgeInsets.all(0),
onPressed: () async { onPressed: () async {
await showDialog<void>( await showDialog<void>(
builder: (_) => MenuWidget( builder: (_) => MenuWidget(walletViewModel),
name: walletViewModel.name,
subname: walletViewModel.subname,
type: walletViewModel.type),
context: context); context: context);
}, },
child: menuButton child: menuButton

View file

@ -6,8 +6,10 @@ import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
// FIXME: terrible design
class WalletMenu { class WalletMenu {
WalletMenu(this.context); WalletMenu(this.context, this.reconnect);
final List<String> items = [ final List<String> items = [
S.current.reconnect, S.current.reconnect,
@ -30,6 +32,7 @@ class WalletMenu {
]; ];
final BuildContext context; final BuildContext context;
final Future<void> Function() reconnect;
void action(int index) { void action(int index) {
switch (index) { switch (index) {
@ -70,8 +73,6 @@ class WalletMenu {
} }
Future<void> _presentReconnectAlert(BuildContext context) async { Future<void> _presentReconnectAlert(BuildContext context) async {
final walletStore = Provider.of<WalletStore>(context);
await showDialog<void>( await showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -80,12 +81,11 @@ class WalletMenu {
alertContent: S.of(context).reconnect_alert_text, alertContent: S.of(context).reconnect_alert_text,
leftButtonText: S.of(context).ok, leftButtonText: S.of(context).ok,
rightButtonText: S.of(context).cancel, rightButtonText: S.of(context).cancel,
actionLeftButton: () { actionLeftButton: () async {
walletStore.reconnect(); await reconnect?.call();
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
actionRightButton: () => Navigator.of(context).pop() actionRightButton: () => Navigator.of(context).pop());
);
}); });
} }
} }

View file

@ -1,14 +1,15 @@
import 'dart:ui'; import 'dart:ui';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart'; import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart'; import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
class MenuWidget extends StatefulWidget { // FIXME: terrible design.
MenuWidget({this.type, this.name, this.subname});
final WalletType type; class MenuWidget extends StatefulWidget {
final String name; MenuWidget(this.dashboardViewModel);
final String subname;
final DashboardViewModel dashboardViewModel;
@override @override
MenuWidgetState createState() => MenuWidgetState(); MenuWidgetState createState() => MenuWidgetState();
@ -65,8 +66,8 @@ class MenuWidgetState extends State<MenuWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final walletMenu = WalletMenu(context); final walletMenu =
// final walletStore = Provider.of<WalletStore>(context); WalletMenu(context, () async => widget.dashboardViewModel.reconnect());
final itemCount = walletMenu.items.length; final itemCount = walletMenu.items.length;
return Row( return Row(
@ -118,19 +119,20 @@ class MenuWidgetState extends State<MenuWidget> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[ children: <Widget>[
_iconFor(type: widget.type), _iconFor(type: widget.dashboardViewModel.type),
SizedBox(width: 16), SizedBox(width: 16),
Expanded( Expanded(
child: Container( child: Container(
height: 40, height: 40,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: widget.subname != null mainAxisAlignment:
widget.dashboardViewModel.subname != null
? MainAxisAlignment.spaceBetween ? MainAxisAlignment.spaceBetween
: MainAxisAlignment.center, : MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Text( Text(
widget.name, widget.dashboardViewModel.name,
style: TextStyle( style: TextStyle(
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme .primaryTextTheme
@ -141,9 +143,9 @@ class MenuWidgetState extends State<MenuWidget> {
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold), fontWeight: FontWeight.bold),
), ),
if (widget.subname != null) if (widget.dashboardViewModel.subname != null)
Text( Text(
widget.subname, widget.dashboardViewModel.subname,
style: TextStyle( style: TextStyle(
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme .primaryTextTheme

View file

@ -74,15 +74,41 @@ class NodeListPage extends BasePage {
} }
final node = nodeListViewModel.nodes[index]; final node = nodeListViewModel.nodes[index];
final isSelected = index == 1; // FIXME: hardcoded value.
final nodeListRow = NodeListRow( final nodeListRow = NodeListRow(
title: node.uri, title: node.value.uri,
isSelected: isSelected, isSelected: node.isSelected,
isAlive: node.requestNode(), isAlive: node.value.requestNode(),
onTap: (_) {}); onTap: (_) async {
if (node.isSelected) {
return;
}
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(
S.of(context).change_current_node(node.value.uri),
textAlign: TextAlign.center,
),
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)),
],
);
});
});
final dismissibleRow = Dismissible( final dismissibleRow = Dismissible(
key: Key('${node.key}'), key: Key('${node.value.key}'),
confirmDismiss: (direction) async { confirmDismiss: (direction) async {
return await showDialog( return await showDialog(
context: context, context: context,
@ -99,7 +125,7 @@ class NodeListPage extends BasePage {
}); });
}, },
onDismissed: (direction) async => onDismissed: (direction) async =>
nodeListViewModel.delete(node), nodeListViewModel.delete(node.value),
direction: DismissDirection.endToStart, direction: DismissDirection.endToStart,
background: Container( background: Container(
padding: EdgeInsets.only(right: 10.0), padding: EdgeInsets.only(right: 10.0),
@ -120,7 +146,7 @@ class NodeListPage extends BasePage {
)), )),
child: nodeListRow); child: nodeListRow);
return isSelected ? nodeListRow : dismissibleRow; return node.isSelected ? nodeListRow : dismissibleRow;
}, },
itemCounter: (int sectionIndex) { itemCounter: (int sectionIndex) {
if (sectionIndex == 0) { if (sectionIndex == 0) {

View file

@ -63,14 +63,6 @@ class WalletListBodyState extends State<WalletListBody> {
itemBuilder: (__, index) { itemBuilder: (__, index) {
final wallet = widget.walletListViewModel.wallets[index]; final wallet = widget.walletListViewModel.wallets[index];
final screenWidth = MediaQuery.of(context).size.width; final screenWidth = MediaQuery.of(context).size.width;
// String shortAddress = '';
// if (wallet.isCurrent) {
// shortAddress = wallet.address;
// shortAddress = shortAddress.replaceRange(
// 4, shortAddress.length - 4, '...');
// }
final walletMenu = WalletMenu(context, widget.walletListViewModel); final walletMenu = WalletMenu(context, widget.walletListViewModel);
final items = final items =
walletMenu.generateItemsForWalletMenu(wallet.isCurrent); walletMenu.generateItemsForWalletMenu(wallet.isCurrent);

View file

@ -109,7 +109,7 @@ class WalletMenu {
try { try {
auth.changeProcessText( auth.changeProcessText(
S.of(context).wallet_list_removing_wallet(wallet.name)); S.of(context).wallet_list_removing_wallet(wallet.name));
// await _walletListStore.remove(wallet); await walletListViewModel.remove(wallet);
auth.close(); auth.close();
} catch (e) { } catch (e) {
auth.changeProcessText(S auth.changeProcessText(S

View file

@ -42,7 +42,7 @@ abstract class SettingsStoreBase with Store {
_sharedPreferences = sharedPreferences; _sharedPreferences = sharedPreferences;
_nodes = nodes; _nodes = nodes;
allowBiometricalAuthentication = initialAllowBiometricalAuthentication; allowBiometricalAuthentication = initialAllowBiometricalAuthentication;
isDarkTheme = initialDarkTheme; isDarkTheme = true;
defaultPinLength = initialPinLength; defaultPinLength = initialPinLength;
languageCode = initialLanguageCode; languageCode = initialLanguageCode;
currentLocale = initialCurrentLocale; currentLocale = initialCurrentLocale;
@ -143,7 +143,7 @@ abstract class SettingsStoreBase with Store {
bool allowBiometricalAuthentication; bool allowBiometricalAuthentication;
@observable @observable
bool isDarkTheme; bool isDarkTheme = true;
@observable @observable
int defaultPinLength; int defaultPinLength;
@ -285,7 +285,7 @@ abstract class SettingsStoreBase with Store {
} }
Future setCurrentNodeToDefault() async { Future setCurrentNodeToDefault() async {
await changeCurrentNodeToDefault(sharedPreferences: _sharedPreferences, nodes: _nodes); // await changeCurrentNodeToDefault(sharedPreferences: _sharedPreferences, nodes: _nodes);
await loadSettings(); await loadSettings();
} }

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
@ -29,8 +30,9 @@ abstract class SettingsStoreBase with Store {
@required int initialPinLength, @required int initialPinLength,
@required String initialLanguageCode, @required String initialLanguageCode,
@required String initialCurrentLocale, @required String initialCurrentLocale,
@required this.node, // @required this.node,
@required this.appVersion, @required this.appVersion,
@required Map<WalletType, Node> nodes,
this.actionlistDisplayMode}) { this.actionlistDisplayMode}) {
fiatCurrency = initialFiatCurrency; fiatCurrency = initialFiatCurrency;
transactionPriority = initialTransactionPriority; transactionPriority = initialTransactionPriority;
@ -44,9 +46,11 @@ abstract class SettingsStoreBase with Store {
itemHeaders = {}; itemHeaders = {};
_sharedPreferences = sharedPreferences; _sharedPreferences = sharedPreferences;
_nodeSource = nodeSource; _nodeSource = nodeSource;
_nodes = nodes;
} }
static const currentNodeIdKey = 'current_node_id'; static const currentNodeIdKey = 'current_node_id';
static const currentBitcoinElectrumSererIdKey = 'current_node_id_btc';
static const currentFiatCurrencyKey = 'current_fiat_currency'; static const currentFiatCurrencyKey = 'current_fiat_currency';
static const currentTransactionPriorityKey = 'current_fee_priority'; static const currentTransactionPriorityKey = 'current_fee_priority';
static const currentBalanceDisplayModeKey = 'current_balance_display_mode'; static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
@ -58,8 +62,8 @@ abstract class SettingsStoreBase with Store {
static const currentPinLength = 'current_pin_length'; static const currentPinLength = 'current_pin_length';
static const currentLanguageCode = 'language_code'; static const currentLanguageCode = 'language_code';
@observable // @observable
Node node; // Node node;
@observable @observable
FiatCurrency fiatCurrency; FiatCurrency fiatCurrency;
@ -97,6 +101,26 @@ abstract class SettingsStoreBase with Store {
SharedPreferences _sharedPreferences; SharedPreferences _sharedPreferences;
Box<Node> _nodeSource; Box<Node> _nodeSource;
Map<WalletType, Node> _nodes;
Node getCurrentNode(WalletType walletType) => _nodes[walletType];
Future<void> setCurrentNode(Node node, WalletType walletType) async {
switch (walletType) {
case WalletType.bitcoin:
await _sharedPreferences.setInt(
currentBitcoinElectrumSererIdKey, node.key as int);
break;
case WalletType.monero:
await _sharedPreferences.setInt(currentNodeIdKey, node.key as int);
break;
default:
break;
}
_nodes[walletType] = node;
}
static Future<SettingsStore> load( static Future<SettingsStore> load(
{@required Box<Node> nodeSource, {@required Box<Node> nodeSource,
FiatCurrency initialFiatCurrency = FiatCurrency.usd, FiatCurrency initialFiatCurrency = FiatCurrency.usd,
@ -126,12 +150,18 @@ abstract class SettingsStoreBase with Store {
await Language.localeDetection(); await Language.localeDetection();
final initialCurrentLocale = await Devicelocale.currentLocale; final initialCurrentLocale = await Devicelocale.currentLocale;
final nodeId = sharedPreferences.getInt(currentNodeIdKey); final nodeId = sharedPreferences.getInt(currentNodeIdKey);
final node = nodeSource.get(nodeId); final bitcoinElectrumServerId =
sharedPreferences.getInt(currentBitcoinElectrumSererIdKey);
final moneroNode = nodeSource.get(nodeId);
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
final packageInfo = await PackageInfo.fromPlatform(); final packageInfo = await PackageInfo.fromPlatform();
return SettingsStore( return SettingsStore(
sharedPreferences: sharedPreferences, sharedPreferences: sharedPreferences,
node: node, nodes: {
WalletType.monero: moneroNode,
WalletType.bitcoin: bitcoinElectrumServer
},
nodeSource: nodeSource, nodeSource: nodeSource,
appVersion: packageInfo.version, appVersion: packageInfo.version,
initialFiatCurrency: currentFiatCurrency, initialFiatCurrency: currentFiatCurrency,

45
lib/utils/mobx.dart Normal file
View file

@ -0,0 +1,45 @@
import 'package:mobx/mobx.dart';
Dispose connectDifferent<T, Y>(ObservableList<T> source, ObservableList<Y> dest,
Y Function(T) transform, {bool Function(T) filter}) {
return source.observe((change) {
switch (change.type) {
case OperationType.add:
final _values = change.added;
Iterable<T> values;
if (filter != null) {
values = _values.where(filter);
}
dest.addAll(values.map((e) => transform(e)));
break;
case OperationType.remove:
print(change.index);
print(change.removed);
change.removed.forEach((element) { dest.remove(element); });
// dest.removeAt(change.index);
break;
case OperationType.update:
// change.index
break;
}
});
}
Dispose connect<T>(ObservableList<T> source, ObservableList<T> dest) {
return source.observe((change) {
switch (change.type) {
case OperationType.add:
dest.addAll(change.added);
break;
case OperationType.remove:
dest.removeAt(change.index);
break;
case OperationType.update:
// change.index
break;
}
});
}

View file

@ -129,6 +129,11 @@ abstract class DashboardViewModelBase with Store {
ReactionDisposer _reaction; ReactionDisposer _reaction;
Future<void> reconnect() async {
final node = appStore.settingsStore.getCurrentNode(wallet.type);
await wallet.connectToNode(node: node);
}
void _onWalletChange(WalletBase wallet) { void _onWalletChange(WalletBase wallet) {
name = wallet.name; name = wallet.name;
transactions.clear(); transactions.clear();

View file

@ -1,26 +1,92 @@
import 'package:flutter/foundation.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/wallet_base.dart'; import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/src/domain/common/node.dart'; import 'package:cake_wallet/src/domain/common/node.dart';
import 'package:cake_wallet/src/domain/common/node_list.dart'; import 'package:cake_wallet/src/domain/common/node_list.dart';
import 'package:cake_wallet/store/node_list_store.dart'; import 'package:cake_wallet/store/node_list_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/src/domain/common/default_settings_migration.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/utils/mobx.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 { class ItemCell<Item> {
NodeListViewModelBase(this._nodeListStore, this._nodeSource, this._wallet); ItemCell(this.value, {@required this.isSelected});
@computed final Item value;
ObservableList<Node> get nodes => ObservableList<Node>.of( final bool isSelected;
_nodeListStore.nodes.where((node) => node.type == _wallet.type)); }
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)));
connectDifferent(
_nodeListStore.nodes,
nodes,
(Node val) =>
ItemCell<Node>(val, isSelected: val.key == currentNode.key),
filter: (Node val) {
return val.type == _wallet.type;
});
}
ObservableList<ItemCell<Node>> nodes;
final WalletBase _wallet; final WalletBase _wallet;
final Box<Node> _nodeSource; final Box<Node> _nodeSource;
final NodeListStore _nodeListStore; final NodeListStore _nodeListStore;
final SettingsStore _settingsStore;
Future<void> reset() async => await resetToDefault(_nodeSource); Future<void> reset() async {
await resetToDefault(_nodeSource);
Future<void> delete(Node node) async => node.delete(); Node node;
switch (_wallet.type) {
case WalletType.bitcoin:
node = getBitcoinDefaultElectrumServer(nodes: _nodeSource);
break;
case WalletType.monero:
node = getMoneroDefaultNode(
nodes: _nodeSource,
);
break;
default:
break;
}
await _wallet.connectToNode(node: node);
}
Future<void> delete(Node node) async => _nodeSource.delete(node.key);
Future<void> setAsCurrent(Node node) async {
await _wallet.connectToNode(node: node);
await _settingsStore.setCurrentNode(node, _wallet.type);
_updateCurrentNode();
}
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);
}
}
}
} }

View file

@ -27,6 +27,7 @@ abstract class SendViewModelBase with Store {
this._wallet, this._settingsStore, this._fiatConversationStore) this._wallet, this._settingsStore, this._fiatConversationStore)
: state = InitialSendViewModelState(), : state = InitialSendViewModelState(),
_cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12, _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12,
// FIXME: need to be based on wallet type.
all = false; all = false;
@observable @observable
@ -79,7 +80,7 @@ abstract class SendViewModelBase with Store {
final WalletBase _wallet; final WalletBase _wallet;
final SettingsStore _settingsStore; final SettingsStore _settingsStore;
final FiatConvertationStore _fiatConversationStore; final FiatConvertationStore _fiatConversationStore;
NumberFormat _cryptoNumberFormat; final NumberFormat _cryptoNumberFormat;
@action @action
void setAll() => all = true; void setAll() => all = true;
@ -129,7 +130,8 @@ abstract class SendViewModelBase with Store {
void _updateFiatAmount() { void _updateFiatAmount() {
try { try {
final fiat = calculateFiatAmount( final fiat = calculateFiatAmount(
price: _fiatConversationStore.price, cryptoAmount: cryptoAmount); price: _fiatConversationStore.price,
cryptoAmount: cryptoAmount.replaceAll(',', '.'));
if (fiatAmount != fiat) { if (fiatAmount != fiat) {
fiatAmount = fiat; fiatAmount = fiat;
} }
@ -141,7 +143,8 @@ abstract class SendViewModelBase with Store {
@action @action
void _updateCryptoAmount() { void _updateCryptoAmount() {
try { try {
final crypto = double.parse(fiatAmount) / _fiatConversationStore.price; final crypto = double.parse(fiatAmount.replaceAll(',', '.')) /
_fiatConversationStore.price;
final cryptoAmountTmp = _cryptoNumberFormat.format(crypto); final cryptoAmountTmp = _cryptoNumberFormat.format(crypto);
if (cryptoAmount != cryptoAmountTmp) { if (cryptoAmount != cryptoAmountTmp) {

View file

@ -23,7 +23,8 @@ class SettingsViewModel = SettingsViewModelBase with _$SettingsViewModel;
abstract class SettingsViewModelBase with Store { abstract class SettingsViewModelBase with Store {
SettingsViewModelBase(this._settingsStore, WalletBase wallet) SettingsViewModelBase(this._settingsStore, WalletBase wallet)
: itemHeaders = {} { : itemHeaders = {},
_walletType = wallet.type {
sections = [ sections = [
[ [
PickerListItem( PickerListItem(
@ -117,7 +118,7 @@ abstract class SettingsViewModelBase with Store {
} }
@computed @computed
Node get node => _settingsStore.node; Node get node => _settingsStore.getCurrentNode(_walletType);
@computed @computed
FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency; FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency;
@ -150,13 +151,10 @@ abstract class SettingsViewModelBase with Store {
set allowBiometricalAuthentication(bool value) => set allowBiometricalAuthentication(bool value) =>
_settingsStore.allowBiometricalAuthentication = value; _settingsStore.allowBiometricalAuthentication = value;
// @observable
// @observable
final Map<String, String> itemHeaders; final Map<String, String> itemHeaders;
List<List<SettingsListItem>> sections;
final SettingsStore _settingsStore; final SettingsStore _settingsStore;
final WalletType _walletType;
List<List<SettingsListItem>> sections;
@action @action
void toggleTransactionsDisplay() => void toggleTransactionsDisplay() =>

View file

@ -3,9 +3,10 @@ import 'package:cake_wallet/src/domain/common/wallet_type.dart';
class WalletListItem { class WalletListItem {
const WalletListItem( const WalletListItem(
{@required this.name, @required this.type, this.isCurrent = false}); {@required this.name, @required this.type, @required this.key, this.isCurrent = false});
final String name; final String name;
final WalletType type; final WalletType type;
final bool isCurrent; final bool isCurrent;
final dynamic key;
} }

View file

@ -17,11 +17,7 @@ abstract class WalletListViewModelBase with Store {
WalletListViewModelBase( WalletListViewModelBase(
this._walletInfoSource, this._appStore, this._keyService) { this._walletInfoSource, this._appStore, this._keyService) {
wallets = ObservableList<WalletListItem>(); wallets = ObservableList<WalletListItem>();
wallets.addAll(_walletInfoSource.values.map((info) => WalletListItem( _updateList();
name: info.name,
type: info.type,
isCurrent: info.name == _appStore.wallet.name &&
info.type == _appStore.wallet.type)));
} }
@observable @observable
@ -40,7 +36,12 @@ abstract class WalletListViewModelBase with Store {
} }
@action @action
Future<void> remove(WalletListItem wallet) async {} Future<void> remove(WalletListItem wallet) async {
final walletService = _getWalletService(wallet.type);
await walletService.remove(wallet.name);
await _walletInfoSource.delete(wallet.key);
_updateList();
}
WalletService _getWalletService(WalletType type) { WalletService _getWalletService(WalletType type) {
switch (type) { switch (type) {
@ -52,4 +53,14 @@ abstract class WalletListViewModelBase with Store {
return null; return null;
} }
} }
void _updateList() {
wallets.clear();
wallets.addAll(_walletInfoSource.values.map((info) => WalletListItem(
name: info.name,
type: info.type,
key: info.key,
isCurrent: info.name == _appStore.wallet.name &&
info.type == _appStore.wallet.type)));
}
} }