pow node changes

This commit is contained in:
fosse 2023-08-09 21:05:24 -04:00
parent 32554b4cc8
commit d3c76b6d55
14 changed files with 632 additions and 170 deletions

View file

@ -22,6 +22,7 @@ class NanoClient {
StreamSubscription<Transfer>? subscription;
Node? _node;
Node? _powNode;
bool connect(Node node) {
try {
@ -32,6 +33,15 @@ class NanoClient {
}
}
bool connectPow(Node node) {
try {
_powNode = node;
return true;
} catch (e) {
return false;
}
}
Future<NanoBalance> getBalance(String address) async {
final response = await http.post(
_node!.uri,
@ -117,7 +127,7 @@ class NanoClient {
Future<String> requestWork(String hash) async {
return http
.post(
Uri.parse("https://rpc.nano.to"), // TODO: make a setting
_powNode!.uri,
headers: {'Content-type': 'application/json'},
body: json.encode(
{

View file

@ -134,6 +134,11 @@ abstract class NanoWalletBase
}
}
@override
Future<void> connectToPowNode({required Node node}) async {
_client.connectPow(node);
}
@override
Future<PendingTransaction> createTransaction(Object credentials) async {
credentials = credentials as NanoTransactionCredentials;
@ -237,9 +242,7 @@ abstract class NanoWalletBase
id: transactionModel.hash,
amountRaw: transactionModel.amount,
height: transactionModel.height,
direction: transactionModel.account == address
? TransactionDirection.outgoing
: TransactionDirection.incoming,
direction: transactionModel.type == "send" ? TransactionDirection.outgoing : TransactionDirection.incoming,
confirmed: transactionModel.confirmed,
date: transactionModel.date ?? DateTime.now(),
confirmations: transactionModel.confirmed ? 1 : 0,

View file

@ -27,6 +27,7 @@ import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
import 'package:cake_wallet/src/screens/settings/manage_pow_nodes_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
@ -68,6 +69,8 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart';
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
import 'package:cake_wallet/view_model/node_list/pow_node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
@ -212,7 +215,6 @@ final getIt = GetIt.instance;
var _isSetupFinished = false;
late Box<WalletInfo> _walletInfoSource;
late Box<Node> _nodeSource;
late Box<Node> _powNodeSource;
late Box<Contact> _contactSource;
late Box<Trade> _tradesSource;
late Box<Template> _templates;
@ -259,6 +261,7 @@ Future setup({
final settingsStore = await SettingsStoreBase.load(
nodeSource: _nodeSource,
powNodeSource: _nodeSource,
isBitcoinBuyEnabled: isBitcoinBuyEnabled,
// Enforce darkTheme on platforms other than mobile till the design for other themes is completed
initialTheme: ResponsiveLayoutUtil.instance.isMobile && DeviceInfo.instance.isMobile
@ -687,7 +690,12 @@ Future setup({
getIt.registerFactory(() {
final appStore = getIt.get<AppStore>();
return NodeListViewModel(_nodeSource, _powNodeSource, appStore);
return NodeListViewModel(_nodeSource, appStore);
});
getIt.registerFactory(() {
final appStore = getIt.get<AppStore>();
return PowNodeListViewModel(_nodeSource, appStore);
});
getIt.registerFactory(() => ConnectionSyncPage(getIt.get<DashboardViewModel>()));
@ -707,6 +715,10 @@ Future setup({
NodeCreateOrEditViewModel(
_nodeSource, type ?? getIt.get<AppStore>().wallet!.type, getIt.get<SettingsStore>()));
getIt.registerFactoryParam<PowNodeCreateOrEditViewModel, WalletType?, void>(
(WalletType? type, _) => PowNodeCreateOrEditViewModel(
_nodeSource, type ?? getIt.get<AppStore>().wallet!.type, getIt.get<SettingsStore>()));
getIt.registerFactoryParam<NodeCreateOrEditPage, Node?, bool?>(
(Node? editingNode, bool? isSelected) => NodeCreateOrEditPage(
nodeCreateOrEditViewModel: getIt.get<NodeCreateOrEditViewModel>(),
@ -1068,6 +1080,8 @@ Future setup({
);
getIt.registerFactory<ManageNodesPage>(() => ManageNodesPage(getIt.get<NodeListViewModel>()));
getIt.registerFactory<ManagePowNodesPage>(
() => ManagePowNodesPage(getIt.get<PowNodeListViewModel>()));
_isSetupFinished = true;
}

View file

@ -13,4 +13,11 @@ void startOnCurrentNodeChangeReaction(AppStore appStore) {
print(e.toString());
}
});
appStore.settingsStore.powNodes.observe((change) async {
try {
await appStore.wallet!.connectToPowNode(node: change.newValue!);
} catch (e) {
print(e.toString());
}
});
}

View file

@ -21,8 +21,8 @@ ReactionDisposer? _onCurrentWalletChangeReaction;
ReactionDisposer? _onCurrentWalletChangeFiatRateUpdateReaction;
//ReactionDisposer _onCurrentWalletAddressChangeReaction;
void startCurrentWalletChangeReaction(AppStore appStore,
SettingsStore settingsStore, FiatConversionStore fiatConversionStore) {
void startCurrentWalletChangeReaction(
AppStore appStore, SettingsStore settingsStore, FiatConversionStore fiatConversionStore) {
_onCurrentWalletChangeReaction?.reaction.dispose();
_onCurrentWalletChangeFiatRateUpdateReaction?.reaction.dispose();
//_onCurrentWalletAddressChangeReaction?.reaction?dispose();
@ -48,8 +48,8 @@ void startCurrentWalletChangeReaction(AppStore appStore,
//}
//});
_onCurrentWalletChangeReaction = reaction((_) => appStore.wallet, (WalletBase<
Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
_onCurrentWalletChangeReaction = reaction((_) => appStore.wallet,
(WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
wallet) async {
try {
if (wallet == null) {
@ -57,14 +57,19 @@ void startCurrentWalletChangeReaction(AppStore appStore,
}
final node = settingsStore.getCurrentNode(wallet.type);
startWalletSyncStatusChangeReaction(wallet, fiatConversionStore);
startCheckConnectionReaction(wallet, settingsStore);
await getIt.get<SharedPreferences>().setString(PreferencesKey.currentWalletName, wallet.name);
await getIt
.get<SharedPreferences>()
.setString(PreferencesKey.currentWalletName, wallet.name);
await getIt.get<SharedPreferences>().setInt(
PreferencesKey.currentWalletType, serializeToInt(wallet.type));
.setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
await wallet.connectToNode(node: node);
if (wallet.type == WalletType.nano || wallet.type == WalletType.banano) {
final powNode = settingsStore.getCurrentPowNode(wallet.type);
await wallet.connectToPowNode(node: powNode);
}
if (wallet.type == WalletType.haven) {
await updateHavenRate(fiatConversionStore);
@ -82,9 +87,8 @@ void startCurrentWalletChangeReaction(AppStore appStore,
}
});
_onCurrentWalletChangeFiatRateUpdateReaction =
reaction((_) => appStore.wallet, (WalletBase<Balance,
TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
_onCurrentWalletChangeFiatRateUpdateReaction = reaction((_) => appStore.wallet,
(WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
wallet) async {
try {
if (wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) {
@ -92,8 +96,7 @@ void startCurrentWalletChangeReaction(AppStore appStore,
}
fiatConversionStore.prices[wallet.currency] = 0;
fiatConversionStore.prices[wallet.currency] =
await FiatConversionService.fetchPrice(
fiatConversionStore.prices[wallet.currency] = await FiatConversionService.fetchPrice(
crypto: wallet.currency,
fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);

View file

@ -21,6 +21,7 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart
import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
import 'package:cake_wallet/src/screens/settings/manage_pow_nodes_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
@ -573,6 +574,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.manageNodes:
return MaterialPageRoute<void>(builder: (_) => getIt.get<ManageNodesPage>());
case Routes.managePowNodes:
return MaterialPageRoute<void>(builder: (_) => getIt.get<ManagePowNodesPage>());
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(

View file

@ -92,4 +92,6 @@ class Routes {
static const homeSettings = '/home_settings';
static const editToken = '/edit_token';
static const manageNodes = '/manage_nodes';
static const managePowNodes = '/manage_pow_nodes';
}

View file

@ -65,6 +65,17 @@ class ConnectionSyncPage extends BasePage {
title: S.current.manage_nodes,
handler: (context) => Navigator.of(context).pushNamed(Routes.manageNodes),
),
Observer(
builder: (context) {
if (!dashboardViewModel.hasPowNodes)
return const SizedBox();
return SettingsCellWithArrow(
title: S.current.manage_nodes,
handler: (context) => Navigator.of(context).pushNamed(Routes.managePowNodes),
);
},
),
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
],
),

View file

@ -0,0 +1,86 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class ManagePowNodesPage extends BasePage {
ManagePowNodesPage(this.nodeListViewModel);
final PowNodeListViewModel nodeListViewModel;
@override
String get title => S.current.manage_nodes;
@override
Widget body(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 10),
child: Column(
children: [
Semantics(
button: true,
child: NodeHeaderListRow(
title: S.of(context).add_new_node,
onTap: (_) async => await Navigator.of(context).pushNamed(Routes.newNode),
),
),
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
SizedBox(height: 20),
Observer(
builder: (BuildContext context) {
return Flexible(
child: SectionStandardList(
sectionCount: 1,
context: context,
dividerPadding: EdgeInsets.symmetric(horizontal: 24),
itemCounter: (int sectionIndex) {
return nodeListViewModel.nodes.length;
},
itemBuilder: (_, sectionIndex, index) {
final node = nodeListViewModel.nodes[index];
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
final nodeListRow = NodeListRow(
title: node.uriRaw,
node: node,
isSelected: isSelected,
onTap: (_) async {
if (isSelected) {
return;
}
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).change_current_node_title,
alertContent: nodeListViewModel.getAlertContent(node.uriRaw),
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();
},
);
});
},
);
return nodeListRow;
},
),
);
},
),
],
),
);
}
}

View file

@ -82,6 +82,7 @@ abstract class SettingsStoreBase with Store {
TransactionPriority? initialLitecoinTransactionPriority,
TransactionPriority? initialEthereumTransactionPriority})
: nodes = ObservableMap<WalletType, Node>.of(nodes),
powNodes = ObservableMap<WalletType, Node>.of(nodes),
_sharedPreferences = sharedPreferences,
_backgroundTasks = backgroundTasks,
fiatCurrency = initialFiatCurrency,
@ -456,6 +457,7 @@ abstract class SettingsStoreBase with Store {
final BackgroundTasks _backgroundTasks;
ObservableMap<WalletType, Node> nodes;
ObservableMap<WalletType, Node> powNodes;
Node getCurrentNode(WalletType walletType) {
final node = nodes[walletType];
@ -467,6 +469,16 @@ abstract class SettingsStoreBase with Store {
return node;
}
Node getCurrentPowNode(WalletType walletType) {
final node = powNodes[walletType];
if (node == null) {
throw Exception('No pow node found for wallet type: ${walletType.toString()}');
}
return node;
}
bool isBitcoinBuyEnabled;
bool get shouldShowReceiveWarning =>
@ -477,6 +489,7 @@ abstract class SettingsStoreBase with Store {
static Future<SettingsStore> load(
{required Box<Node> nodeSource,
required Box<Node> powNodeSource,
required bool isBitcoinBuyEnabled,
FiatCurrency initialFiatCurrency = FiatCurrency.usd,
BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance,
@ -577,8 +590,7 @@ abstract class SettingsStoreBase with Store {
SortBalanceBy.values[sharedPreferences.getInt(PreferencesKey.sortBalanceBy) ?? 0];
final pinNativeTokenAtTop =
sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true;
final useEtherscan =
sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
// If no value
if (pinLength == null || pinLength == 0) {
@ -601,11 +613,13 @@ abstract class SettingsStoreBase with Store {
final havenNode = nodeSource.get(havenNodeId);
final ethereumNode = nodeSource.get(ethereumNodeId);
final nanoNode = nodeSource.get(nanoNodeId);
final nanoPowNode = powNodeSource.get(nanoNodeId);
final packageInfo = await PackageInfo.fromPlatform();
final deviceName = await _getDeviceName() ?? '';
final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true;
final nodes = <WalletType, Node>{};
final powNodes = <WalletType, Node>{};
if (moneroNode != null) {
nodes[WalletType.monero] = moneroNode;
@ -630,6 +644,9 @@ abstract class SettingsStoreBase with Store {
if (nanoNode != null) {
nodes[WalletType.nano] = nanoNode;
}
if (nanoPowNode != null) {
powNodes[WalletType.nano] = nanoPowNode;
}
final savedSyncMode = SyncMode.all.firstWhere((element) {
return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 1);

View file

@ -75,28 +75,28 @@ abstract class DashboardViewModelBase with Store {
FilterItem(
value: () => tradeFilterStore.displayAllTrades,
caption: S.current.all_trades,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.all)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.all)),
FilterItem(
value: () => tradeFilterStore.displayChangeNow,
caption: ExchangeProviderDescription.changeNow.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
FilterItem(
value: () => tradeFilterStore.displaySideShift,
caption: ExchangeProviderDescription.sideShift.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.sideShift)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.sideShift)),
FilterItem(
value: () => tradeFilterStore.displaySimpleSwap,
caption: ExchangeProviderDescription.simpleSwap.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)),
FilterItem(
value: () => tradeFilterStore.displayTrocador,
caption: ExchangeProviderDescription.trocador.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.trocador)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.trocador)),
]
},
subname = '',
@ -118,15 +118,17 @@ abstract class DashboardViewModelBase with Store {
if (_wallet.type == WalletType.monero) {
subname = monero!.getCurrentAccount(_wallet).label;
_onMoneroAccountChangeReaction = reaction((_) => monero!.getMoneroWalletDetails(wallet)
.account, (Account account) => _onMoneroAccountChange(_wallet));
_onMoneroAccountChangeReaction = reaction(
(_) => monero!.getMoneroWalletDetails(wallet).account,
(Account account) => _onMoneroAccountChange(_wallet));
_onMoneroBalanceChangeReaction = reaction((_) => monero!.getMoneroWalletDetails(wallet).balance,
_onMoneroBalanceChangeReaction = reaction(
(_) => monero!.getMoneroWalletDetails(wallet).balance,
(MoneroBalance balance) => _onMoneroTransactionsUpdate(_wallet));
final _accountTransactions = _wallet
.transactionHistory.transactions.values
.where((tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
final _accountTransactions = _wallet.transactionHistory.transactions.values
.where((tx) =>
monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
.toList();
transactions = ObservableList.of(_accountTransactions.map((transaction) =>
@ -135,9 +137,8 @@ abstract class DashboardViewModelBase with Store {
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
} else {
transactions = ObservableList.of(wallet
.transactionHistory.transactions.values
.map((transaction) => TransactionListItem(
transactions = ObservableList.of(wallet.transactionHistory.transactions.values.map(
(transaction) => TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
@ -151,15 +152,15 @@ abstract class DashboardViewModelBase with Store {
(TransactionInfo? transaction) => TransactionListItem(
transaction: transaction!,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore),
filter: (TransactionInfo? transaction) {
settingsStore: appStore.settingsStore), filter: (TransactionInfo? transaction) {
if (transaction == null) {
return false;
}
final wallet = _wallet;
if (wallet.type == WalletType.monero) {
return monero!.getTransactionInfoAccountId(transaction) == monero!.getCurrentAccount(wallet).id;
return monero!.getTransactionInfoAccountId(transaction) ==
monero!.getCurrentAccount(wallet).id;
}
return true;
@ -209,8 +210,7 @@ abstract class DashboardViewModelBase with Store {
}
@computed
BalanceDisplayMode get balanceDisplayMode =>
appStore.settingsStore.balanceDisplayMode;
BalanceDisplayMode get balanceDisplayMode => appStore.settingsStore.balanceDisplayMode;
@computed
bool get shouldShowMarketPlaceInDashboard {
@ -218,14 +218,12 @@ abstract class DashboardViewModelBase with Store {
}
@computed
List<TradeListItem> get trades => tradesStore.trades
.where((trade) => trade.trade.walletId == wallet.id)
.toList();
List<TradeListItem> get trades =>
tradesStore.trades.where((trade) => trade.trade.walletId == wallet.id).toList();
@computed
List<OrderListItem> get orders => ordersStore.orders
.where((item) => item.order.walletId == wallet.id)
.toList();
List<OrderListItem> get orders =>
ordersStore.orders.where((item) => item.order.walletId == wallet.id).toList();
@computed
List<AnonpayTransactionListItem> get anonpayTransactons => anonpayTransactionsStore.transactions
@ -239,7 +237,8 @@ abstract class DashboardViewModelBase with Store {
List<ActionListItem> get items {
final _items = <ActionListItem>[];
_items.addAll(transactionFilterStore.filtered(transactions: [...transactions, ...anonpayTransactons]));
_items.addAll(
transactionFilterStore.filtered(transactions: [...transactions, ...anonpayTransactons]));
_items.addAll(tradeFilterStore.filtered(trades: trades, wallet: wallet));
_items.addAll(orders);
@ -247,8 +246,7 @@ abstract class DashboardViewModelBase with Store {
}
@observable
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
wallet;
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
bool get hasRescan => wallet.type == WalletType.monero || wallet.type == WalletType.haven;
@ -277,8 +275,7 @@ abstract class DashboardViewModelBase with Store {
bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup;
@action
void furtherShowYatPopup(bool shouldShow) =>
settingsStore.shouldShowYatPopup = shouldShow;
void furtherShowYatPopup(bool shouldShow) => settingsStore.shouldShowYatPopup = shouldShow;
@computed
bool get isEnabledExchangeAction => settingsStore.exchangeStatus != ExchangeApiMode.disabled;
@ -287,8 +284,7 @@ abstract class DashboardViewModelBase with Store {
bool hasExchangeAction;
@computed
bool get isEnabledBuyAction =>
!settingsStore.disableBuy && wallet.type != WalletType.haven;
bool get isEnabledBuyAction => !settingsStore.disableBuy && wallet.type != WalletType.haven;
@observable
bool hasBuyAction;
@ -309,16 +305,21 @@ abstract class DashboardViewModelBase with Store {
@observable
bool isOutdatedElectrumWallet;
@computed
bool get hasPowNodes => wallet.type == WalletType.nano || wallet.type != WalletType.banano;
Future<void> reconnect() async {
final node = appStore.settingsStore.getCurrentNode(wallet.type);
await wallet.connectToNode(node: node);
if (hasPowNodes) {
final powNode = settingsStore.getCurrentPowNode(wallet.type);
await wallet.connectToPowNode(node: powNode);
}
}
@action
void _onWalletChange(
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
TransactionInfo>?
wallet) {
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>? wallet) {
if (wallet == null) {
return;
}
@ -336,10 +337,12 @@ abstract class DashboardViewModelBase with Store {
_onMoneroAccountChangeReaction?.reaction.dispose();
_onMoneroBalanceChangeReaction?.reaction.dispose();
_onMoneroAccountChangeReaction = reaction((_) => monero!.getMoneroWalletDetails(wallet)
.account, (Account account) => _onMoneroAccountChange(wallet));
_onMoneroAccountChangeReaction = reaction(
(_) => monero!.getMoneroWalletDetails(wallet).account,
(Account account) => _onMoneroAccountChange(wallet));
_onMoneroBalanceChangeReaction = reaction((_) => monero!.getMoneroWalletDetails(wallet).balance,
_onMoneroBalanceChangeReaction = reaction(
(_) => monero!.getMoneroWalletDetails(wallet).balance,
(MoneroBalance balance) => _onMoneroTransactionsUpdate(wallet));
_onMoneroTransactionsUpdate(wallet);
@ -350,8 +353,8 @@ abstract class DashboardViewModelBase with Store {
transactions.clear();
transactions.addAll(wallet.transactionHistory.transactions.values.map(
(transaction) => TransactionListItem(
transactions.addAll(wallet.transactionHistory.transactions.values.map((transaction) =>
TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
@ -360,12 +363,10 @@ abstract class DashboardViewModelBase with Store {
connectMapToListWithTransform(
appStore.wallet!.transactionHistory.transactions,
transactions,
(TransactionInfo? transaction)
=> TransactionListItem(
(TransactionInfo? transaction) => TransactionListItem(
transaction: transaction!,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore),
filter: (TransactionInfo? tx) {
settingsStore: appStore.settingsStore), filter: (TransactionInfo? tx) {
if (tx == null) {
return false;
}
@ -388,12 +389,15 @@ abstract class DashboardViewModelBase with Store {
void _onMoneroTransactionsUpdate(WalletBase wallet) {
transactions.clear();
final _accountTransactions = monero!.getTransactionHistory(wallet).transactions.values
.where((tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
final _accountTransactions = monero!
.getTransactionHistory(wallet)
.transactions
.values
.where(
(tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
.toList();
transactions.addAll(_accountTransactions.map((transaction) =>
TransactionListItem(
transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));

View file

@ -15,9 +15,8 @@ part 'node_list_view_model.g.dart';
class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel;
abstract class NodeListViewModelBase with Store {
NodeListViewModelBase(this._nodeSource, this._powNodeSource, this._appStore)
NodeListViewModelBase(this._nodeSource, this._appStore)
: nodes = ObservableList<Node>(),
powNodes = ObservableList<Node>(),
settingsStore = _appStore.settingsStore {
_bindNodes();
@ -41,11 +40,9 @@ abstract class NodeListViewModelBase with Store {
S.current.change_current_node(uri) +
'${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + S.current.orbot_running_alert : ''}';
final SettingsStore settingsStore;
final ObservableList<Node> nodes;
final ObservableList<Node> powNodes;
final SettingsStore settingsStore;
final Box<Node> _nodeSource;
final Box<Node> _powNodeSource;
final AppStore _appStore;
Future<void> reset() async {

View file

@ -0,0 +1,211 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/entities/qr_scanner.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:collection/collection.dart';
part 'pow_node_create_or_edit_view_model.g.dart';
class PowNodeCreateOrEditViewModel = PowNodeCreateOrEditViewModelBase
with _$PowNodeCreateOrEditViewModel;
abstract class PowNodeCreateOrEditViewModelBase with Store {
PowNodeCreateOrEditViewModelBase(
this._nodeSource, this._walletType, this._settingsStore)
: state = InitialExecutionState(),
connectionState = InitialExecutionState(),
useSSL = false,
address = '',
port = '',
login = '',
password = '',
trusted = false,
useSocksProxy = false,
socksProxyAddress = '';
@observable
ExecutionState state;
@observable
String address;
@observable
String port;
@observable
String login;
@observable
String password;
@observable
ExecutionState connectionState;
@observable
bool useSSL;
@observable
bool trusted;
@observable
bool useSocksProxy;
@observable
String socksProxyAddress;
@computed
bool get isReady => address.isNotEmpty && port.isNotEmpty;
bool get hasAuthCredentials =>
_walletType == WalletType.monero || _walletType == WalletType.haven;
String get uri {
var uri = address;
if (port.isNotEmpty) {
uri += ':' + port;
}
return uri;
}
final WalletType _walletType;
final Box<Node> _nodeSource;
final SettingsStore _settingsStore;
@action
void reset() {
address = '';
port = '';
login = '';
password = '';
useSSL = false;
trusted = false;
useSocksProxy = false;
socksProxyAddress = '';
}
@action
void setPort(String val) => port = val;
@action
void setAddress(String val) => address = val;
@action
void setLogin(String val) => login = val;
@action
void setPassword(String val) => password = val;
@action
void setSSL(bool val) => useSSL = val;
@action
void setTrusted(bool val) => trusted = val;
@action
void setSocksProxy(bool val) => useSocksProxy = val;
@action
void setSocksProxyAddress(String val) => socksProxyAddress = val;
@action
Future<void> save({Node? editingNode, bool saveAsCurrent = false}) async {
final node = Node(
uri: uri,
type: _walletType,
login: login,
password: password,
useSSL: useSSL,
trusted: trusted,
socksProxyAddress: socksProxyAddress);
try {
state = IsExecutingState();
if (editingNode != null) {
await _nodeSource.put(editingNode.key, node);
} else if (_existingNode(node) != null) {
setAsCurrent(_existingNode(node)!);
} else {
await _nodeSource.add(node);
setAsCurrent(_nodeSource.values.last);
}
if (saveAsCurrent) {
setAsCurrent(node);
}
state = ExecutedSuccessfullyState();
} catch (e) {
state = FailureState(e.toString());
}
}
@action
Future<void> connect() async {
final node = Node(
uri: uri,
type: _walletType,
login: login,
password: password,
useSSL: useSSL,
trusted: trusted,
socksProxyAddress: socksProxyAddress);
try {
connectionState = IsExecutingState();
final isAlive = await node.requestNode();
connectionState = ExecutedSuccessfullyState(payload: isAlive);
} catch (e) {
connectionState = FailureState(e.toString());
}
}
Node? _existingNode(Node node) {
final nodes = _nodeSource.values.toList();
nodes.forEach((item) {
item.login ??= '';
item.password ??= '';
item.useSSL ??= false;
});
return nodes.firstWhereOrNull((item) => item == node);
}
@action
void setAsCurrent(Node node) => _settingsStore.nodes[_walletType] = node;
@action
Future<void> scanQRCodeForNewNode() async {
try {
String code = await presentQRScanner();
if (code.isEmpty) {
throw Exception('Unexpected scan QR code value: value is empty');
}
final uri = Uri.tryParse(code);
if (uri == null) {
throw Exception('Unexpected scan QR code value: Value is invalid');
}
final userInfo = uri.userInfo.split(':');
if (userInfo.length < 2) {
throw Exception('Unexpected scan QR code value: Value is invalid');
}
final rpcUser = userInfo[0];
final rpcPassword = userInfo[1];
final ipAddress = uri.host;
final port = uri.port.toString();
setAddress(ipAddress);
setPassword(rpcPassword);
setLogin(rpcUser);
setPort(port);
} on Exception catch (e) {
connectionState = FailureState(e.toString());
}
}
}

View file

@ -0,0 +1,93 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/utils/mobx.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/node.dart';
import 'package:cake_wallet/entities/node_list.dart';
import 'package:cake_wallet/entities/default_settings_migration.dart';
import 'package:cw_core/wallet_type.dart';
part 'pow_node_list_view_model.g.dart';
class PowNodeListViewModel = PowNodeListViewModelBase with _$PowNodeListViewModel;
abstract class PowNodeListViewModelBase with Store {
PowNodeListViewModelBase(this._nodeSource, this._appStore)
: nodes = ObservableList<Node>(),
settingsStore = _appStore.settingsStore {
_bindNodes();
reaction((_) => _appStore.wallet, (WalletBase? _wallet) {
_bindNodes();
});
}
@computed
Node get currentNode {
final node = settingsStore.nodes[_appStore.wallet!.type];
if (node == null) {
throw Exception('No node for wallet type: ${_appStore.wallet!.type}');
}
return node;
}
String getAlertContent(String uri) =>
S.current.change_current_node(uri) +
'${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + S.current.orbot_running_alert : ''}';
final ObservableList<Node> nodes;
final SettingsStore settingsStore;
final Box<Node> _nodeSource;
final AppStore _appStore;
Future<void> reset() async {
await resetToDefault(_nodeSource);
Node node;
switch (_appStore.wallet!.type) {
case WalletType.bitcoin:
node = getBitcoinDefaultElectrumServer(nodes: _nodeSource)!;
break;
case WalletType.monero:
node = getMoneroDefaultNode(nodes: _nodeSource);
break;
case WalletType.litecoin:
node = getLitecoinDefaultElectrumServer(nodes: _nodeSource)!;
break;
case WalletType.haven:
node = getHavenDefaultNode(nodes: _nodeSource)!;
break;
case WalletType.ethereum:
node = getEthereumDefaultNode(nodes: _nodeSource)!;
break;
case WalletType.nano:
node = getNanoDefaultNode(nodes: _nodeSource)!;
break;
default:
throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}');
}
await setAsCurrent(node);
}
@action
Future<void> delete(Node node) async => node.delete();
Future<void> setAsCurrent(Node node) async => settingsStore.nodes[_appStore.wallet!.type] = node;
@action
void _bindNodes() {
nodes.clear();
_nodeSource.bindToList(
nodes,
filter: (val) => val.type == _appStore.wallet!.type,
initialFire: true,
);
}
}