fix pow node edits (#3078)

malik1004x committed Mar 13, 2026 at 23:20 UTC 9a6fd46e041a969b4805ff91f9c1d47b235d1e4f
3 files changed +112 -128
lib/router.dart
+3 -2
@@ -629,9 +629,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
629
630 case Routes.newPowNode:
631 final args = settings.arguments as Map<String, dynamic>?;
632 + final page = getIt.get<PowNodeCreateOrEditPage>(
633 + param1: args?['editingNode'] as Node?, param2: args?['isSelected'] as bool?);
634 return CupertinoPageRoute<void>(
633 - builder: (_) => getIt.get<PowNodeCreateOrEditPage>(
634 - param1: args?['editingNode'] as Node?, param2: args?['isSelected'] as bool?));
635 + builder: (_) => page);
636
637 case Routes.accountCreation:
638 return CupertinoPageRoute<String>(
lib/src/screens/nodes/pow_node_create_or_edit_page.dart
+105 -123
@@ -1,7 +1,10 @@
1 import 'package:cake_wallet/core/execution_state.dart';
2 +import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
3 +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
4 import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
5 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
6 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
7 +import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
8 import 'package:cake_wallet/utils/show_pop_up.dart';
9 import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
10 import 'package:cw_core/node.dart';
@@ -13,76 +16,30 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
16 import 'package:cake_wallet/src/screens/base_page.dart';
17 import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
18
16 -class PowNodeCreateOrEditPage extends BasePage {
19 +class PowNodeCreateOrEditPage extends StatefulWidget {
20 PowNodeCreateOrEditPage(
21 {required this.nodeCreateOrEditViewModel, this.editingNode, this.isSelected})
19 - : _addressController = TextEditingController(),
20 - _portController = TextEditingController(),
21 - _loginController = TextEditingController(),
22 - _passwordController = TextEditingController() {
23 - reaction((_) => nodeCreateOrEditViewModel.address, (String address) {
24 - if (address != _addressController.text) {
25 - _addressController.text = address;
26 - }
27 - });
28 -
29 - reaction((_) => nodeCreateOrEditViewModel.port, (String port) {
30 - if (port != _portController.text) {
31 - _portController.text = port;
32 - }
33 - });
34 -
35 - if (nodeCreateOrEditViewModel.hasAuthCredentials) {
36 - reaction((_) => nodeCreateOrEditViewModel.login, (String login) {
37 - if (login != _loginController.text) {
38 - _loginController.text = login;
39 - }
40 - });
22 + ;
23
42 - reaction((_) => nodeCreateOrEditViewModel.password, (String password) {
43 - if (password != _passwordController.text) {
44 - _passwordController.text = password;
45 - }
46 - });
47 - }
24
49 - _addressController
50 - .addListener(() => nodeCreateOrEditViewModel.address = _addressController.text);
51 - _portController.addListener(() => nodeCreateOrEditViewModel.port = _portController.text);
52 - _loginController.addListener(() => nodeCreateOrEditViewModel.login = _loginController.text);
53 - _passwordController
54 - .addListener(() => nodeCreateOrEditViewModel.password = _passwordController.text);
55 - }
56 -
57 - final TextEditingController _addressController;
58 - final TextEditingController _portController;
59 - final TextEditingController _loginController;
60 - final TextEditingController _passwordController;
61 -
62 - @override
63 - String get title => editingNode != null ? S.current.edit_node : S.current.node_new;
25
65 - @override
66 - Widget trailing(BuildContext context) => IconButton(
67 - onPressed: () async {
68 - await nodeCreateOrEditViewModel.scanQRCodeForNewNode(context);
69 - },
70 - splashColor: Colors.transparent,
71 - highlightColor: Colors.transparent,
72 - hoverColor: Colors.transparent,
73 - icon: Image.asset(
74 - 'assets/images/qr_code_icon.png',
75 - ),
76 - );
26
27 final NodeCreateOrEditViewModel nodeCreateOrEditViewModel;
28 final Node? editingNode;
29 final bool? isSelected;
30 +
31 + @override
32 + State<PowNodeCreateOrEditPage> createState() => _PowNodeCreateOrEditPageState();
33 +}
34 +
35 +class _PowNodeCreateOrEditPageState extends State<PowNodeCreateOrEditPage> {
36 final _formKey = GlobalKey<NodeFormState>();
37
38 +
39 @override
84 - Widget body(BuildContext context) {
85 - reaction((_) => nodeCreateOrEditViewModel.connectionState, (ExecutionState state) {
40 + void initState() {
41 + super.initState();
42 + reaction((_) => widget.nodeCreateOrEditViewModel.connectionState, (ExecutionState state) {
43 if (state is ExecutedSuccessfullyState) {
44 WidgetsBinding.instance.addPostFrameCallback((_) {
45 showPopUp<void>(
@@ -111,73 +68,98 @@ class PowNodeCreateOrEditPage extends BasePage {
68 });
69 }
70 });
71 + }
72
115 - return Container(
116 - padding: EdgeInsets.only(left: 24, right: 24),
117 - child: ScrollableWithBottomSection(
118 - contentPadding: EdgeInsets.only(bottom: 24.0, top: 8),
119 - content: NodeForm(
120 - key: _formKey,
121 - nodeViewModel: nodeCreateOrEditViewModel,
122 - ),
123 - bottomSectionPadding: EdgeInsets.only(bottom: 24),
124 - bottomSection: Observer(
125 - builder: (_) => Row(
126 - mainAxisAlignment: MainAxisAlignment.center,
127 - children: <Widget>[
128 - Flexible(
129 - child: Container(
130 - padding: EdgeInsets.only(right: 8.0),
131 - child: LoadingPrimaryButton(
132 - onPressed: () async {
133 - final confirmed = await showPopUp<bool>(
134 - context: context,
135 - builder: (BuildContext context) {
136 - return AlertWithTwoActions(
137 - alertTitle: S.of(context).remove_node,
138 - alertContent: S.of(context).remove_node_message,
139 - rightButtonText: S.of(context).remove,
140 - leftButtonText: S.of(context).cancel,
141 - actionRightButton: () => Navigator.pop(context, true),
142 - actionLeftButton: () => Navigator.pop(context, false));
143 - }) ??
144 - false;
145 -
146 - if (confirmed) {
147 - await editingNode!.delete();
148 - Navigator.of(context).pop();
149 - }
150 - },
151 - text: S.of(context).delete,
152 - isDisabled: !nodeCreateOrEditViewModel.isReady || (isSelected ?? false),
153 - color: Theme.of(context).colorScheme.errorContainer,
154 - textColor: Theme.of(context).colorScheme.onPrimary,
155 - ),
156 - ),
157 - ),
158 - Flexible(
159 - child: Container(
160 - padding: EdgeInsets.only(left: 8.0),
161 - child: PrimaryButton(
162 - onPressed: () async {
163 - if (_formKey.currentState != null &&
164 - !_formKey.currentState!.validate()) {
165 - return;
166 - }
73 + @override
74 + Widget build(BuildContext context) {
75
168 - await nodeCreateOrEditViewModel.save(
169 - editingNode: editingNode, saveAsCurrent: isSelected ?? false);
170 - Navigator.of(context).pop();
171 - },
172 - text: S.of(context).save,
173 - color: Theme.of(context).colorScheme.primary,
174 - textColor: Theme.of(context).colorScheme.onPrimary,
175 - isDisabled: (!nodeCreateOrEditViewModel.isReady) ||
176 - (nodeCreateOrEditViewModel.connectionState is IsExecutingState),
177 - ),
178 - )),
179 - ],
76 + return Container(
77 + color: Theme.of(context).colorScheme.surface,
78 + child: KeyboardHideOverlay(
79 + child: Column(
80 + children: [
81 + ModalTopBar(
82 + title: widget.editingNode != null ? S.current.edit_node : S.current.node_new,
83 + leadingIcon: Icon(Icons.arrow_back_ios_new),
84 + onLeadingPressed: Navigator.of(context).pop,
85 + trailingIcon: CakeImageWidget(imageUrl:
86 + "assets/new-ui/scan.svg",width:24,height:24,
87 + colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
88 + ),
89 + onTrailingPressed: () => widget.nodeCreateOrEditViewModel.scanQRCodeForNewNode(context),
90 + ),
91 + Expanded(
92 + child: Container(
93 + padding: EdgeInsets.only(left: 24, right: 24),
94 + child: ScrollableWithBottomSection(
95 + contentPadding: EdgeInsets.only(bottom: 24.0, top: 8),
96 + content: NodeForm(
97 + key: _formKey,
98 + nodeViewModel: widget.nodeCreateOrEditViewModel,
99 + ),
100 + bottomSectionPadding: EdgeInsets.only(bottom: 24),
101 + bottomSection: Observer(
102 + builder: (_) => Row(
103 + mainAxisAlignment: MainAxisAlignment.center,
104 + children: <Widget>[
105 + Flexible(
106 + child: Container(
107 + padding: EdgeInsets.only(right: 8.0),
108 + child: LoadingPrimaryButton(
109 + onPressed: () async {
110 + final confirmed = await showPopUp<bool>(
111 + context: context,
112 + builder: (BuildContext context) {
113 + return AlertWithTwoActions(
114 + alertTitle: S.of(context).remove_node,
115 + alertContent: S.of(context).remove_node_message,
116 + rightButtonText: S.of(context).remove,
117 + leftButtonText: S.of(context).cancel,
118 + actionRightButton: () => Navigator.pop(context, true),
119 + actionLeftButton: () => Navigator.pop(context, false));
120 + }) ??
121 + false;
122 +
123 + if (confirmed) {
124 + await widget.editingNode!.delete();
125 + Navigator.of(context).pop();
126 + }
127 + },
128 + text: S.of(context).delete,
129 + isDisabled: !widget.nodeCreateOrEditViewModel.isReady || (widget.isSelected ?? false),
130 + color: Theme.of(context).colorScheme.errorContainer,
131 + textColor: Theme.of(context).colorScheme.onPrimary,
132 + ),
133 + ),
134 + ),
135 + Flexible(
136 + child: Container(
137 + padding: EdgeInsets.only(left: 8.0),
138 + child: PrimaryButton(
139 + onPressed: () async {
140 + if (_formKey.currentState != null &&
141 + !_formKey.currentState!.validate()) {
142 + return;
143 + }
144 +
145 + await widget.nodeCreateOrEditViewModel.save(
146 + editingNode: widget.editingNode, saveAsCurrent: widget.isSelected ?? false);
147 + Navigator.of(context).pop();
148 + },
149 + text: S.of(context).save,
150 + color: Theme.of(context).colorScheme.primary,
151 + textColor: Theme.of(context).colorScheme.onPrimary,
152 + isDisabled: (!widget.nodeCreateOrEditViewModel.isReady) ||
153 + (widget.nodeCreateOrEditViewModel.connectionState is IsExecutingState),
154 + ),
155 + )),
156 + ],
157 + )),
158 )),
181 - ));
159 + ),
160 + ],
161 + ),
162 + ),
163 + );
164 }
165 }
lib/src/screens/settings/manage_nodes_page.dart
+4 -3
@@ -15,7 +15,8 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
15
16 class ManageNodesPage extends BasePage {
17 ManageNodesPage(this.isPow, {this.nodeListViewModel, this.powNodeListViewModel})
18 - : assert((isPow && powNodeListViewModel != null) || (!isPow && nodeListViewModel != null));
18 + : assert((isPow && powNodeListViewModel != null) || (!isPow && nodeListViewModel != null)),
19 + assert(powNodeListViewModel == null || nodeListViewModel == null);
20
21 final NodeListViewModel? nodeListViewModel;
22 final PowNodeListViewModel? powNodeListViewModel;
@@ -34,7 +35,7 @@ class ManageNodesPage extends BasePage {
35 button: true,
36 child: NodeHeaderListRow(
37 title: S.of(context).add_new_node,
37 - onTap: (_) async => await Navigator.of(context).pushNamed(Routes.newNode),
38 + onTap: (_) async => await Navigator.of(context).pushNamed(isPow ? Routes.newPowNode : Routes.newNode),
39 ),
40 ),
41 if (FeatureFlag.isAutomaticNodeSwitchingEnabled)
@@ -80,7 +81,7 @@ class ManageNodesPage extends BasePage {
81 title: node.uriRaw,
82 node: node,
83 isSelected: isSelected,
83 - isPow: false,
84 + isPow: isPow,
85 onTap: (_) async {
86 if (isSelected) {
87 return;