mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
new-node-list-ui-on-dev (#3259)
* node list new ui # Conflicts: # cw_core/lib/node_list.dart * increase touch target sizes * fix scroll behavior * fix concurrent modification * Update lib/src/screens/settings/manage_nodes_page.dart [skip ci] * Update lib/src/screens/nodes/node_create_or_edit_page.dart [skip ci] * run speed test in parallel not sequential --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
+3
-13
@@ -1118,9 +1118,9 @@ Future<void> setup({
|
||||
|
||||
getIt.registerFactory(() => AddressListPage(getIt.get<WalletAddressListViewModel>()));
|
||||
|
||||
getIt.registerLazySingleton(() {
|
||||
getIt.registerFactoryParam<NodeListViewModel, bool, void>((isPow, _) {
|
||||
final appStore = getIt.get<AppStore>();
|
||||
return NodeListViewModel(appStore);
|
||||
return NodeListViewModel(appStore, isPow);
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<NewAddressesPage, bool, void>(
|
||||
@@ -1131,11 +1131,6 @@ Future<void> setup({
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerLazySingleton(() {
|
||||
final appStore = getIt.get<AppStore>();
|
||||
return PowNodeListViewModel(appStore);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => ConnectionSyncViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!));
|
||||
|
||||
getIt.registerFactory(() => ConnectionSyncPage(getIt.get<ConnectionSyncViewModel>()));
|
||||
@@ -1178,8 +1173,6 @@ Future<void> setup({
|
||||
final Node? editingNode = args['editingNode'] as Node?;
|
||||
return NodeCreateOrEditViewModel(
|
||||
isPow,
|
||||
getIt.get<NodeListViewModel>(),
|
||||
getIt.get<PowNodeListViewModel>(),
|
||||
type,
|
||||
getIt.get<SettingsStore>(),
|
||||
editingNode: editingNode
|
||||
@@ -1700,10 +1693,7 @@ Future<void> setup({
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<ManageNodesPage, bool, void>((bool isPow, _) {
|
||||
if (isPow) {
|
||||
return ManageNodesPage(isPow, powNodeListViewModel: getIt.get<PowNodeListViewModel>(), dashboardViewModel: getIt.get<DashboardViewModel>());
|
||||
}
|
||||
return ManageNodesPage(isPow, nodeListViewModel: getIt.get<NodeListViewModel>(), dashboardViewModel: getIt.get<DashboardViewModel>());
|
||||
return ManageNodesPage(isPow, nodeListViewModel: getIt.get<NodeListViewModel>(param1: isPow), dashboardViewModel: getIt.get<DashboardViewModel>());
|
||||
});
|
||||
|
||||
getIt.registerFactory(
|
||||
|
||||
@@ -17,10 +17,10 @@ import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/lnurl.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
import '../../../pages/receive_page.dart';
|
||||
@@ -198,7 +198,12 @@ class CoinActionRow extends StatelessWidget {
|
||||
Navigator.of(context)
|
||||
.pushNamed(Routes.walletConnectConnectionsListing, arguments: Uri.parse(code));
|
||||
return;
|
||||
} else {
|
||||
} else if(["http", "https", "tcp"].contains(Uri.tryParse(code)?.scheme)) {
|
||||
Navigator.of(context).pushNamed(Routes.newNode,
|
||||
arguments: {"editingNode": Node.fromUri(Uri.parse(code), walletType)});
|
||||
return;
|
||||
|
||||
}else {
|
||||
final uri = Uri.tryParse(code);
|
||||
if (uri == null) return;
|
||||
req = PaymentRequest.fromUri(uri);
|
||||
|
||||
@@ -4,11 +4,12 @@ import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class ModalHeader extends StatelessWidget {
|
||||
const ModalHeader(
|
||||
{super.key, required this.iconPath, required this.message, required this.title});
|
||||
{super.key, required this.iconPath, required this.message, required this.title, this.bottomWidget});
|
||||
|
||||
final String iconPath;
|
||||
final String title;
|
||||
final String message;
|
||||
final Widget? bottomWidget;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -41,7 +42,11 @@ class ModalHeader extends StatelessWidget {
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
textAlign: TextAlign.center,
|
||||
))
|
||||
)),
|
||||
if(bottomWidget != null) ...[
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 18), child: Container(height:1, color: Theme.of(context).colorScheme.surfaceContainerHigh),),
|
||||
bottomWidget!
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/node_share_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class NodeCreateOrEditPage extends StatefulWidget {
|
||||
@@ -76,6 +76,14 @@ class _NodeCreateOrEditPageState extends State<NodeCreateOrEditPage> {
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
reaction((_)=>widget.nodeCreateOrEditViewModel.state, (state) async {
|
||||
if(state is ExecutedSuccessfullyState) {
|
||||
await Future.delayed(Duration(milliseconds: 100));
|
||||
if (mounted) Navigator.of(context).pop(widget.nodeCreateOrEditViewModel.editingNode);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -90,86 +98,62 @@ class _NodeCreateOrEditPageState extends State<NodeCreateOrEditPage> {
|
||||
title: widget.editingNode != null ? S.current.edit_node : S.current.node_new,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
trailingIcon: CakeImageWidget(imageUrl:
|
||||
"assets/new-ui/scan.svg",width:24,height:24,
|
||||
colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
|
||||
),
|
||||
onTrailingPressed: () => widget.nodeCreateOrEditViewModel.scanQRCodeForNewNode(context),
|
||||
trailingIcon: Icon(Icons.check),
|
||||
onTrailingPressed: () async {
|
||||
if (_nodeFormKey.currentState != null && !_nodeFormKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await widget.nodeCreateOrEditViewModel
|
||||
.save(saveAsCurrent: widget.isSelected ?? false);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: KeyboardHideOverlay(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 18, right: 18),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: const EdgeInsets.only(bottom: 24.0, top: 8),
|
||||
content: NodeForm(
|
||||
child: Column(
|
||||
spacing: 18,
|
||||
children: [NodeForm(
|
||||
key: _nodeFormKey,
|
||||
nodeViewModel: widget.nodeCreateOrEditViewModel,
|
||||
),
|
||||
bottomSectionPadding: const EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
final confirmed = await showPopUp<bool>(
|
||||
context: context,
|
||||
builder: (context) => 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),
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
NewListSections(sections: {"":[
|
||||
if(widget.editingNode != null)
|
||||
|
||||
if (confirmed) {
|
||||
await widget.nodeCreateOrEditViewModel.delete(editingNode: widget.editingNode!);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
text: S.of(context).delete,
|
||||
isDisabled: widget.editingNode == null ||
|
||||
!widget.nodeCreateOrEditViewModel.isReady ||
|
||||
widget.editingNode!.isBuiltin ||
|
||||
(widget.isSelected ?? false),
|
||||
color: Theme.of(context).colorScheme.errorContainer,
|
||||
textColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (_nodeFormKey.currentState != null && !_nodeFormKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
ListItemRegularRow(keyValue: "share", label: S.of(context).share_this_node, foregroundColor: Theme.of(context).colorScheme.primary, showArrow: false, onTap: (){
|
||||
|
||||
await widget.nodeCreateOrEditViewModel.save(
|
||||
saveAsCurrent: widget.isSelected ?? false);
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isDisabled: (!widget.nodeCreateOrEditViewModel.isReady) ||
|
||||
(widget.nodeCreateOrEditViewModel.connectionState is IsExecutingState),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Navigator.of(context).push(CupertinoPageRoute(builder: (context)=>NodeSharePage(uri: widget.editingNode!.uri, currency: walletTypeToCryptoCurrency(widget.type!))));
|
||||
|
||||
|
||||
}),
|
||||
if(!(widget.editingNode == null ||
|
||||
!widget.nodeCreateOrEditViewModel.isReady ||
|
||||
widget.editingNode!.isBuiltin ||
|
||||
(widget.isSelected ?? false)))
|
||||
ListItemRegularRow(keyValue: "delete", label: S.of(context).delete_this_node, foregroundColor: Theme.of(context).colorScheme.errorContainer, showArrow: false, onTap: ()async{
|
||||
final confirmed = await showPopUp<bool>(
|
||||
context: context,
|
||||
builder: (context) => 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),
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
|
||||
if (confirmed) {
|
||||
await widget.nodeCreateOrEditViewModel.delete(editingNode: widget.editingNode!);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
})
|
||||
]})
|
||||
|
||||
]
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NodeSharePage extends StatelessWidget {
|
||||
const NodeSharePage({super.key, required this.uri, required this.currency});
|
||||
|
||||
final Uri uri;
|
||||
final CryptoCurrency currency;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final qrSize = MediaQuery.of(context).size.width * 0.786;
|
||||
final padding = 16.0;
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: S.of(context).share,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 40,
|
||||
children: [
|
||||
Stack(alignment: Alignment.center, children: [
|
||||
SizedBox(
|
||||
width: qrSize,
|
||||
height: qrSize,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
child: QrImage(
|
||||
size: qrSize - padding * 2,
|
||||
embeddedImagePath: "",
|
||||
data: uri.toString())),
|
||||
),
|
||||
SizedBox(
|
||||
width: 60,
|
||||
height: 60,
|
||||
child: Stack(
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/settings_row_icons/nodes.svg",
|
||||
width: 54,
|
||||
height: 54,
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: CakeImageWidget(
|
||||
imageUrl: currency.iconPath,
|
||||
width: 24,
|
||||
height: 24,
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
]),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 36.0),
|
||||
child: Text(S.of(context).node_share_desc,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant)),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,115 +1,55 @@
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NodeListRow extends StandardListRow {
|
||||
class NodeListRow extends StatelessWidget {
|
||||
NodeListRow(
|
||||
{required String title,
|
||||
String? subtitle,
|
||||
required this.node,
|
||||
required void Function(BuildContext context) onTap,
|
||||
required bool isSelected,
|
||||
required this.isPow})
|
||||
: super(title: title, onTap: onTap, isSelected: isSelected, subtitle: subtitle);
|
||||
{required this.node,
|
||||
required this.onTap,
|
||||
required this.onEditComplete,
|
||||
required this.isSelected,
|
||||
required this.isPow, this.speed});
|
||||
|
||||
final Node node;
|
||||
final bool isPow;
|
||||
final VoidCallback onTap;
|
||||
final Future<void> Function(Object?) onEditComplete;
|
||||
final bool isSelected;
|
||||
final NodeSpeed? speed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final leading = buildLeading(context);
|
||||
final trailing = buildTrailing(context);
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 18, right: 18, top: 2, bottom: 2),
|
||||
margin: EdgeInsets.only(top: 2, bottom: 2),
|
||||
child: FilledButton(
|
||||
onPressed: () => onTap?.call(context),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
leading,
|
||||
buildCenter(context, hasLeftOffset: true),
|
||||
trailing,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildLeading(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: node.requestNode(),
|
||||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.done:
|
||||
return NodeIndicator(isLive: snapshot.data ?? false);
|
||||
default:
|
||||
return NodeIndicator(isLive: false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) {
|
||||
final hasLabel = node.label != null && node.label!.isNotEmpty;
|
||||
final uriText = "${node.uri.host}:${node.uri.port}";
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
isPow ? Routes.newPowNode : Routes.newNode,
|
||||
arguments: {'editingNode': node, 'isSelected': isSelected},
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.edit,
|
||||
size: 14,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NodeHeaderListRow extends StandardListRow {
|
||||
NodeHeaderListRow({required String title, required void Function(BuildContext context) onTap})
|
||||
: super(title: title, onTap: onTap, isSelected: false);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final leading = buildLeading(context);
|
||||
final trailing = buildTrailing(context);
|
||||
return Container(
|
||||
height: 56,
|
||||
padding: EdgeInsets.only(left: 12, right: 12, top: 2, bottom: 2),
|
||||
child: TextButton(
|
||||
onPressed: () => onTap?.call(context),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Theme.of(context).colorScheme.surface),
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
),
|
||||
),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 12),
|
||||
child: Row(
|
||||
spacing: 12,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
if (leading != null) leading,
|
||||
buildCenter(context, hasLeftOffset: leading != null),
|
||||
leading,
|
||||
Expanded(
|
||||
child: Column(crossAxisAlignment:CrossAxisAlignment.start,spacing:4,children: [
|
||||
Row(
|
||||
spacing: 4,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(hasLabel ? node.label! : uriText, style: TextStyle(fontSize: 12),),
|
||||
if(badgePath != null)
|
||||
CakeImageWidget(imageUrl: badgePath, colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary,BlendMode.srcIn),)
|
||||
],
|
||||
),
|
||||
if(hasLabel) Text(uriText, style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),),
|
||||
],),
|
||||
),
|
||||
trailing,
|
||||
],
|
||||
),
|
||||
@@ -117,12 +57,44 @@ class NodeHeaderListRow extends StandardListRow {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildLeading(BuildContext context) => isSelected
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Icon(
|
||||
Icons.check,
|
||||
size: 16,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
)
|
||||
: SizedBox(width: 24, height: 24);
|
||||
|
||||
Widget buildTrailing(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 20,
|
||||
child: Icon(Icons.add,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant, size: 24.0),
|
||||
return Row(
|
||||
children: [
|
||||
if (speed != null) CakeImageWidget(imageUrl: speed!.iconPath, width: 24, height: 24),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () async {
|
||||
final res = await Navigator.of(context).pushNamed(isPow ? Routes.newPowNode : Routes.newNode,
|
||||
arguments: {'editingNode': node, 'isSelected': isSelected});
|
||||
await onEditComplete(res);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/3dots_vertical.svg",
|
||||
colorFilter:
|
||||
ColorFilter.mode(Theme.of(context).colorScheme.onSurfaceVariant, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String? get badgePath {
|
||||
if(node.isOfficial) return "assets/new-ui/official_node.svg";
|
||||
if(node.isBuiltin) return "assets/new-ui/default_node.svg";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modal_header.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.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/screens/settings/widgets/settings_switcher_cell.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/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.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:cw_core/node.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class ManageNodesPage extends BasePage {
|
||||
ManageNodesPage(this.isPow, {this.nodeListViewModel, this.powNodeListViewModel, this.dashboardViewModel})
|
||||
: assert((isPow && powNodeListViewModel != null) || (!isPow && nodeListViewModel != null)),
|
||||
assert(powNodeListViewModel == null || nodeListViewModel == null);
|
||||
class ManageNodesPage extends StatefulWidget {
|
||||
ManageNodesPage(this.isPow, {required this.nodeListViewModel, this.dashboardViewModel});
|
||||
|
||||
final DashboardViewModel? dashboardViewModel;
|
||||
final NodeListViewModel? nodeListViewModel;
|
||||
final PowNodeListViewModel? powNodeListViewModel;
|
||||
final NodeListViewModel nodeListViewModel;
|
||||
final bool isPow;
|
||||
|
||||
@override
|
||||
bool get hideAppBar => true;
|
||||
State<ManageNodesPage> createState() => _ManageNodesPageState();
|
||||
}
|
||||
|
||||
class _ManageNodesPageState extends State<ManageNodesPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.delayed(Duration(milliseconds: 300))
|
||||
.then((_) => widget.nodeListViewModel.speedTestNodes());
|
||||
}
|
||||
|
||||
@override
|
||||
String get title => S.current.manage_nodes;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
Widget build(BuildContext context) {
|
||||
return ModalPageWrapper(
|
||||
horizontalPadding: 0,
|
||||
topBar: ModalTopBar(
|
||||
title: S.of(context).manage_nodes,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
@@ -44,87 +44,96 @@ class ManageNodesPage extends BasePage {
|
||||
trailingWidget: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
if (dashboardViewModel?.hasRescan ?? true)
|
||||
ModernButton(
|
||||
Observer(
|
||||
builder: (_) => ModernButton(
|
||||
size: 36,
|
||||
icon: Icon(Icons.history),
|
||||
onPressed: () => Navigator.of(context).pushNamed(Routes.rescan)),
|
||||
icon: widget.nodeListViewModel.isTestingNodeSpeed
|
||||
? CupertinoActivityIndicator()
|
||||
: Icon(Icons.refresh),
|
||||
onPressed: () => widget.nodeListViewModel.speedTestNodes()),
|
||||
),
|
||||
ModernButton(
|
||||
size: 36,
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () => Navigator.of(context).pushNamed(isPow ? Routes.newPowNode : Routes.newNode))
|
||||
onPressed: ()async {
|
||||
final res = await Navigator.of(context)
|
||||
.pushNamed(widget.isPow ? Routes.newPowNode : Routes.newNode);
|
||||
if(res != null && res is Node) {
|
||||
widget.nodeListViewModel.nodes.add(res);
|
||||
}
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
header: ModalHeader(
|
||||
iconPath: "assets/new-ui/settings_row_icons/nodes.svg",
|
||||
message: S.of(context).nodes_desc,
|
||||
title: S.of(context).nodes,
|
||||
bottomWidget: Observer(
|
||||
builder: (_) => NodeListRow(
|
||||
node: widget.nodeListViewModel.currentNode,
|
||||
speed: widget.nodeListViewModel.nodeSpeedFor(widget.nodeListViewModel.currentNode),
|
||||
onEditComplete: (res)async{
|
||||
if(res != null && res is Node) {
|
||||
widget.nodeListViewModel.nodes.removeWhere((item)=>item.id == res.id);
|
||||
widget.nodeListViewModel.nodes.add(res);
|
||||
}
|
||||
|
||||
},
|
||||
onTap: () {},
|
||||
isSelected: true,
|
||||
isPow: widget.isPow)),
|
||||
),
|
||||
content: Column(
|
||||
children: [
|
||||
if (FeatureFlag.isAutomaticNodeSwitchingEnabled)
|
||||
Observer(
|
||||
builder: (_) => SettingsSwitcherCell(
|
||||
key: ValueKey('manage_nodes_page_enable_auto_node_switching_button_key'),
|
||||
title: S.current.enable_auto_node_switching,
|
||||
value: isPow
|
||||
? powNodeListViewModel!.enableAutomaticNodeSwitching
|
||||
: nodeListViewModel!.enableAutomaticNodeSwitching,
|
||||
onValueChange: (BuildContext context, bool value) {
|
||||
if (isPow) {
|
||||
powNodeListViewModel!.setEnableAutomaticNodeSwitching(value);
|
||||
} else {
|
||||
nodeListViewModel!.setEnableAutomaticNodeSwitching(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
if (FeatureFlag.isAutomaticNodeSwitchingEnabled) SizedBox(height: 8),
|
||||
Observer(
|
||||
builder: (BuildContext context) {
|
||||
int itemsCount =
|
||||
nodeListViewModel?.nodes.length ?? powNodeListViewModel!.nodes.length;
|
||||
return SectionStandardList(
|
||||
scrollController: ModalScrollController.of(context),
|
||||
sectionCount: 1,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
dividerPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||
itemCounter: (int sectionIndex) => itemsCount,
|
||||
itemBuilder: (_, index) {
|
||||
return Observer(
|
||||
builder: (context) {
|
||||
final node =
|
||||
nodeListViewModel?.nodes[index] ?? powNodeListViewModel!.nodes[index];
|
||||
late bool isSelected;
|
||||
if (isPow) {
|
||||
isSelected = node.id == powNodeListViewModel!.currentNode.id;
|
||||
} else {
|
||||
isSelected = node.id == nodeListViewModel!.currentNode.id;
|
||||
}
|
||||
final nodeListRow = NodeListRow(
|
||||
title: node.uriRaw,
|
||||
subtitle: node.label ?? '',
|
||||
node: node,
|
||||
isSelected: isSelected,
|
||||
isPow: isPow,
|
||||
onTap: (_) async {
|
||||
if (isSelected) {
|
||||
return;
|
||||
}
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(18)),
|
||||
child: Observer(
|
||||
builder: (BuildContext context) {
|
||||
int itemsCount = widget.nodeListViewModel.nonCurrentNodes.length;
|
||||
return ListView.separated(
|
||||
controller: ModalScrollController.of(context),
|
||||
shrinkWrap: true,
|
||||
physics: BouncingScrollPhysics(),
|
||||
itemCount: itemsCount,
|
||||
separatorBuilder: (context, index) => Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
),
|
||||
itemBuilder: (_, index) {
|
||||
return Observer(
|
||||
builder: (context) {
|
||||
final node = widget.nodeListViewModel.nonCurrentNodes[index];
|
||||
final nodeListRow = NodeListRow(
|
||||
node: node,
|
||||
isSelected: false,
|
||||
isPow: widget.isPow,
|
||||
speed: widget.nodeListViewModel.nodeSpeedFor(node),
|
||||
onEditComplete: (res) async {
|
||||
if(res != null && res is Node) {
|
||||
widget.nodeListViewModel.nodes.removeWhere((item)=>item.id == res.id);
|
||||
widget.nodeListViewModel.nodes.add(res);
|
||||
}
|
||||
|
||||
},
|
||||
onTap: () async {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).change_current_node_title,
|
||||
alertContent: nodeListViewModel?.getAlertContent(node.uriRaw) ??
|
||||
powNodeListViewModel!.getAlertContent(node.uriRaw),
|
||||
alertContent:
|
||||
widget.nodeListViewModel.getAlertContent(node.uriRaw),
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).change,
|
||||
actionLeftButton: () => Navigator.of(context).pop(),
|
||||
actionRightButton: () async {
|
||||
if (isPow) {
|
||||
await powNodeListViewModel!.setAsCurrent(node);
|
||||
} else {
|
||||
await nodeListViewModel!.setAsCurrent(node);
|
||||
}
|
||||
await widget.nodeListViewModel.setAsCurrent(node);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
@@ -132,32 +141,16 @@ class ManageNodesPage extends BasePage {
|
||||
);
|
||||
},
|
||||
);
|
||||
return nodeListRow;
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
return nodeListRow;
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
Future<void> _presentReconnectAlert(BuildContext context) async {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).reconnection,
|
||||
alertContent: S.of(context).reconnect_alert_text,
|
||||
rightButtonText: S.of(context).ok,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () async {
|
||||
Navigator.of(context).pop();
|
||||
await dashboardViewModel!.reconnect();
|
||||
},
|
||||
actionLeftButton: () => Navigator.of(context).pop());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,7 @@ import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item.dart';
|
||||
import 'package:cake_wallet/entities/qr_scanner.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/permission_handler.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_core/node.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:cw_core/utils/proxy_wrapper.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@@ -20,7 +17,7 @@ class NodeCreateOrEditViewModel = NodeCreateOrEditViewModelBase
|
||||
|
||||
abstract class NodeCreateOrEditViewModelBase with Store {
|
||||
NodeCreateOrEditViewModelBase(
|
||||
this.isPow, this.nodeListViewModel,this.powNodeListViewModel, this.walletType, this._settingsStore, {this.editingNode})
|
||||
this.isPow, this.walletType, this._settingsStore, {this.editingNode})
|
||||
: state = InitialExecutionState(),
|
||||
connectionState = InitialExecutionState(),
|
||||
label = editingNode?.label ?? '',
|
||||
@@ -116,8 +113,6 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||
@observable
|
||||
bool isPow;
|
||||
|
||||
final NodeListViewModel? nodeListViewModel;
|
||||
final PowNodeListViewModel? powNodeListViewModel;
|
||||
|
||||
@computed
|
||||
bool get isReady =>
|
||||
@@ -257,12 +252,6 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||
@action
|
||||
Future<void> delete({required Node editingNode}) async {
|
||||
await editingNode.delete();
|
||||
if(nodeListViewModel != null) {
|
||||
nodeListViewModel!.bindNodes();
|
||||
}
|
||||
if(powNodeListViewModel != null) {
|
||||
powNodeListViewModel!.bindNodes();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -287,12 +276,6 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||
}
|
||||
|
||||
state = ExecutedSuccessfullyState();
|
||||
if(nodeListViewModel != null) {
|
||||
nodeListViewModel!.bindNodes();
|
||||
}
|
||||
if(powNodeListViewModel != null) {
|
||||
powNodeListViewModel!.bindNodes();
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
state = FailureState(e.toString());
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/node_list.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.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';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
@@ -14,11 +16,34 @@ import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
|
||||
part 'node_list_view_model.g.dart';
|
||||
|
||||
|
||||
class NodeSpeed {
|
||||
final String iconPath;
|
||||
|
||||
// fraction of best node ping needed to be classified as this speed.
|
||||
final double treshold;
|
||||
|
||||
const NodeSpeed._(this.iconPath, this.treshold);
|
||||
|
||||
static const disconnected = NodeSpeed._(
|
||||
"assets/new-ui/node_speed_badges/disconnected.svg", 0);
|
||||
static const slow = NodeSpeed._(
|
||||
"assets/new-ui/node_speed_badges/slow.svg", 0);
|
||||
static const medium = NodeSpeed._(
|
||||
"assets/new-ui/node_speed_badges/medium.svg", 0.5);
|
||||
static const fast = NodeSpeed._(
|
||||
"assets/new-ui/node_speed_badges/fast.svg", 0.8);
|
||||
|
||||
static const all = [fast, medium, slow, disconnected];
|
||||
}
|
||||
|
||||
|
||||
class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel;
|
||||
|
||||
abstract class NodeListViewModelBase with Store {
|
||||
NodeListViewModelBase(this._appStore)
|
||||
NodeListViewModelBase(this._appStore, this.isPow)
|
||||
: nodes = ObservableList<Node>(),
|
||||
_nodeSpeeds = ObservableMap<String, int>(),
|
||||
settingsStore = _appStore.settingsStore {
|
||||
bindNodes();
|
||||
|
||||
@@ -37,6 +62,7 @@ abstract class NodeListViewModelBase with Store {
|
||||
bindNodes();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@computed
|
||||
Node get currentNode {
|
||||
@@ -52,7 +78,7 @@ abstract class NodeListViewModelBase with Store {
|
||||
return settingsStore.getCurrentNode(walletType, chainId: chainId);
|
||||
}
|
||||
|
||||
final node = settingsStore.nodes[walletType];
|
||||
final node = isPow ? settingsStore.powNodes[walletType] : settingsStore.nodes[walletType];
|
||||
if (node == null) {
|
||||
throw Exception('No node for wallet type: $walletType');
|
||||
}
|
||||
@@ -73,6 +99,59 @@ abstract class NodeListViewModelBase with Store {
|
||||
final ObservableList<Node> nodes;
|
||||
final SettingsStore settingsStore;
|
||||
final AppStore _appStore;
|
||||
final bool isPow;
|
||||
final ObservableMap<String, int> _nodeSpeeds;
|
||||
|
||||
|
||||
@computed
|
||||
List<Node> get nonCurrentNodes => nodes.where((item)=>item != currentNode).toList();
|
||||
|
||||
@observable
|
||||
bool isTestingNodeSpeed = false;
|
||||
|
||||
Future<void> speedTestNodes() async {
|
||||
try {
|
||||
isTestingNodeSpeed = true;
|
||||
_nodeSpeeds.clear();
|
||||
final nodes = this.nodes.toList();
|
||||
await Future.wait(nodes.map((node) async {
|
||||
final sw = Stopwatch()..start();
|
||||
final res = await node
|
||||
.requestNode()
|
||||
.timeout(const Duration(seconds: 10), onTimeout: () => false);
|
||||
sw.stop();
|
||||
if (res) {
|
||||
_nodeSpeeds[node.uriRaw] = sw.elapsedMilliseconds;
|
||||
}
|
||||
}));
|
||||
} finally {
|
||||
isTestingNodeSpeed = false;
|
||||
}
|
||||
}
|
||||
|
||||
NodeSpeed? nodeSpeedFor(Node node) {
|
||||
final bestSpeed = _bestNodeSpeed;
|
||||
final currentSpeed = _nodeSpeeds[node.uriRaw];
|
||||
if (bestSpeed == null) {
|
||||
return null;
|
||||
}
|
||||
if (currentSpeed == null) {
|
||||
return NodeSpeed.disconnected;
|
||||
}
|
||||
for (final speed in NodeSpeed.all) {
|
||||
if (currentSpeed < (bestSpeed / speed.treshold)) return speed;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
int? get _bestNodeSpeed {
|
||||
try {
|
||||
printV(_nodeSpeeds);
|
||||
return _nodeSpeeds.values.reduce(min);
|
||||
} catch (e) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> reset() async {
|
||||
await resetToDefault();
|
||||
@@ -125,7 +204,12 @@ abstract class NodeListViewModelBase with Store {
|
||||
}
|
||||
|
||||
// For non-EVM wallets, use the wallet type directly
|
||||
settingsStore.nodes[walletType] = node;
|
||||
if(isPow) {
|
||||
settingsStore.powNodes[walletType] = node;
|
||||
} else {
|
||||
settingsStore.nodes[walletType] = node;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -150,7 +234,7 @@ abstract class NodeListViewModelBase with Store {
|
||||
}
|
||||
|
||||
// For non-EVM wallets, use the wallet type directly
|
||||
nodes.addAll(await Node.getAllForWalletType(walletType));
|
||||
nodes.addAll(isPow ? await Node.getAllForWalletTypePow(walletType) : await Node.getAllForWalletType(walletType));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:cw_core/node_list.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.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._appStore)
|
||||
: nodes = ObservableList<Node>(),
|
||||
settingsStore = _appStore.settingsStore {
|
||||
bindNodes();
|
||||
|
||||
reaction((_) => _appStore.wallet, (WalletBase? _wallet) {
|
||||
bindNodes();
|
||||
});
|
||||
}
|
||||
|
||||
@computed
|
||||
Node get currentNode {
|
||||
final node = settingsStore.powNodes[_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' + (CakeTor.instance!.enabled ? '' : S.current.orbot_running_alert) : ''}';
|
||||
|
||||
@computed
|
||||
bool get enableAutomaticNodeSwitching => settingsStore.enableAutomaticNodeSwitching;
|
||||
|
||||
@action
|
||||
void setEnableAutomaticNodeSwitching(bool value) =>
|
||||
settingsStore.enableAutomaticNodeSwitching = value;
|
||||
|
||||
final ObservableList<Node> nodes;
|
||||
final SettingsStore settingsStore;
|
||||
final AppStore _appStore;
|
||||
|
||||
Future<void> reset() async {
|
||||
await resetPowToDefault();
|
||||
|
||||
Node node;
|
||||
|
||||
switch (_appStore.wallet!.type) {
|
||||
case WalletType.nano:
|
||||
node = (await getNanoDefaultPowNode())!;
|
||||
break;
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${_appStore.wallet!.type} for pow node list');
|
||||
}
|
||||
|
||||
await setAsCurrent(node);
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> delete(Node node) async => node.delete();
|
||||
|
||||
Future<void> setAsCurrent(Node node) async =>
|
||||
settingsStore.powNodes[_appStore.wallet!.type] = node;
|
||||
|
||||
@action
|
||||
Future<void> bindNodes() async {
|
||||
nodes.clear();
|
||||
nodes.addAll(await Node.getAllPow());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user