From 0bd63512841764815d9831b9efa19362d234e417 Mon Sep 17 00:00:00 2001 From: mik3mast3rs Date: Sat, 28 Jan 2023 14:53:21 +0200 Subject: [PATCH 1/4] Feat: stop using the chain id from the node --- lib/utils/node_utils.dart | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/utils/node_utils.dart b/lib/utils/node_utils.dart index ec808e3e..82b16985 100644 --- a/lib/utils/node_utils.dart +++ b/lib/utils/node_utils.dart @@ -10,7 +10,6 @@ import 'package:zenon_syrius_wallet_flutter/main.dart'; import 'package:zenon_syrius_wallet_flutter/model/model.dart'; import 'package:zenon_syrius_wallet_flutter/utils/constants.dart'; import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; -import 'package:zenon_syrius_wallet_flutter/utils/logger.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; import 'package:znn_sdk_dart/znn_sdk_dart.dart'; @@ -22,17 +21,6 @@ class NodeUtils { url, retry: false, ); - - if (connectionStatus) { - try { - await zenon!.ledger.getFrontierMomentum().then((value) { - setChainIdentifier(chainIdentifier: value.chainIdentifier.toInt()); - }); - } catch (e) { - Logger.logError(e); - } - } - return connectionStatus; } From 0c72b147699b3d8462f1198a95faba230f86d907 Mon Sep 17 00:00:00 2001 From: mik3mast3rs Date: Sat, 28 Jan 2023 15:47:20 +0200 Subject: [PATCH 2/4] Refactor: rename validator method validateNumber to validateAmount --- lib/utils/input_validators.dart | 2 +- .../pillars_widgets/pillars_stepper_container.dart | 2 +- .../sentinel_widgets/sentinels_stepper_container.dart | 2 +- .../modular_widgets/transfer_widgets/receive/receive_large.dart | 2 +- .../transfer_widgets/receive/receive_medium.dart | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/utils/input_validators.dart b/lib/utils/input_validators.dart index 146f9c06..84d269cd 100644 --- a/lib/utils/input_validators.dart +++ b/lib/utils/input_validators.dart @@ -34,7 +34,7 @@ class InputValidators { return (value ?? '').isEmpty ? '$fieldName must not be empty' : null; } - static String? validateNumber(String? value) { + static String? validateAmount(String? value) { if (value != null) { try { if (value.isEmpty) { diff --git a/lib/widgets/modular_widgets/pillars_widgets/pillars_stepper_container.dart b/lib/widgets/modular_widgets/pillars_widgets/pillars_stepper_container.dart index 8b41cb0b..1b68dbb9 100644 --- a/lib/widgets/modular_widgets/pillars_widgets/pillars_stepper_container.dart +++ b/lib/widgets/modular_widgets/pillars_widgets/pillars_stepper_container.dart @@ -705,7 +705,7 @@ class _MainPillarsState extends State { child: InputField( enabled: false, controller: _znnAmountController, - validator: InputValidators.validateNumber, + validator: InputValidators.validateAmount, ), ), ], diff --git a/lib/widgets/modular_widgets/sentinel_widgets/sentinels_stepper_container.dart b/lib/widgets/modular_widgets/sentinel_widgets/sentinels_stepper_container.dart index 6fa748bb..bc7c9787 100644 --- a/lib/widgets/modular_widgets/sentinel_widgets/sentinels_stepper_container.dart +++ b/lib/widgets/modular_widgets/sentinel_widgets/sentinels_stepper_container.dart @@ -560,7 +560,7 @@ class _MainSentinelsState extends State { child: InputField( enabled: false, controller: _znnAmountController, - validator: InputValidators.validateNumber, + validator: InputValidators.validateAmount, ), ), ], diff --git a/lib/widgets/modular_widgets/transfer_widgets/receive/receive_large.dart b/lib/widgets/modular_widgets/transfer_widgets/receive/receive_large.dart index 176afa1b..0b0d91a8 100644 --- a/lib/widgets/modular_widgets/transfer_widgets/receive/receive_large.dart +++ b/lib/widgets/modular_widgets/transfer_widgets/receive/receive_large.dart @@ -120,7 +120,7 @@ class _ReceiveLargeCardState extends State { autovalidateMode: AutovalidateMode.onUserInteraction, child: InputField( - validator: InputValidators.validateNumber, + validator: InputValidators.validateAmount, onChanged: (value) => setState(() {}), inputFormatters: FormatUtils.getAmountTextInputFormatters( diff --git a/lib/widgets/modular_widgets/transfer_widgets/receive/receive_medium.dart b/lib/widgets/modular_widgets/transfer_widgets/receive/receive_medium.dart index 9b50fc72..53640d4a 100644 --- a/lib/widgets/modular_widgets/transfer_widgets/receive/receive_medium.dart +++ b/lib/widgets/modular_widgets/transfer_widgets/receive/receive_medium.dart @@ -125,7 +125,7 @@ class _ReceiveMediumCardState extends State { key: _amountKey, autovalidateMode: AutovalidateMode.onUserInteraction, child: InputField( - validator: InputValidators.validateNumber, + validator: InputValidators.validateAmount, onChanged: (value) => setState(() {}), inputFormatters: FormatUtils.getAmountTextInputFormatters( From 7ed530572767f36921471b95c59df50261c0f8f7 Mon Sep 17 00:00:00 2001 From: mik3mast3rs Date: Sat, 28 Jan 2023 16:52:09 +0200 Subject: [PATCH 3/4] Feat: Allow user to manually change the chain id --- lib/utils/constants.dart | 4 + lib/utils/init_utils.dart | 11 ++ lib/utils/input_validators.dart | 12 +++ .../settings_widgets/node_management.dart | 100 +++++++++++++++++- 4 files changed, 126 insertions(+), 1 deletion(-) diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index f16a2236..6df18d2c 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -172,6 +172,10 @@ const bool kLaunchAtStartupDefaultValue = false; const String kEnableDesktopNotificationsKey = 'enable_desktop_notifications'; const bool kEnableDesktopNotificationsDefaultValue = false; +/// Node management +const String kChainIdKey = 'chain_id'; +const int kChainIdDefaultValue = 1; // 1 corresponds to Alphanet + // Display constants const String kThemeModeKey = 'theme_mode_key'; const ThemeMode kDefaultThemeMode = ThemeMode.dark; diff --git a/lib/utils/init_utils.dart b/lib/utils/init_utils.dart index 417ad2fe..65d62b9c 100644 --- a/lib/utils/init_utils.dart +++ b/lib/utils/init_utils.dart @@ -10,6 +10,7 @@ import 'package:zenon_syrius_wallet_flutter/utils/global.dart'; import 'package:zenon_syrius_wallet_flutter/utils/keystore_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/node_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/widget_utils.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class InitUtils { static Future initApp(BuildContext context) async { @@ -21,12 +22,22 @@ class InitUtils { await KeyStoreUtils.setKeyStorePath(); await _setNumUnlockFailedAttempts(); await NodeUtils.setNode(); + _setChainId(); await NodeUtils.loadDbNodes(); } catch (e) { rethrow; } } + static void _setChainId() { + setChainIdentifier( + chainIdentifier: sharedPrefsService!.get( + kChainIdKey, + defaultValue: kChainIdDefaultValue, + ), + ); + } + static Future _setNumUnlockFailedAttempts() async { if (sharedPrefsService == null) { sharedPrefsService = await sl.getAsync(); diff --git a/lib/utils/input_validators.dart b/lib/utils/input_validators.dart index 84d269cd..19108952 100644 --- a/lib/utils/input_validators.dart +++ b/lib/utils/input_validators.dart @@ -34,6 +34,18 @@ class InputValidators { return (value ?? '').isEmpty ? '$fieldName must not be empty' : null; } + static String? validateNumber(String? number) { + try { + if (number == null) { + return 'Add a number'; + } + int.parse(number); + return null; + } catch (e) { + return 'Input is not a valid number'; + } + } + static String? validateAmount(String? value) { if (value != null) { try { diff --git a/lib/widgets/modular_widgets/settings_widgets/node_management.dart b/lib/widgets/modular_widgets/settings_widgets/node_management.dart index 3d755d28..fac09893 100644 --- a/lib/widgets/modular_widgets/settings_widgets/node_management.dart +++ b/lib/widgets/modular_widgets/settings_widgets/node_management.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'dart:isolate'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; import 'package:wakelock/wakelock.dart'; import 'package:zenon_syrius_wallet_flutter/blocs/blocs.dart'; @@ -14,6 +15,7 @@ import 'package:zenon_syrius_wallet_flutter/utils/input_validators.dart'; import 'package:zenon_syrius_wallet_flutter/utils/node_utils.dart'; import 'package:zenon_syrius_wallet_flutter/utils/notification_utils.dart'; import 'package:zenon_syrius_wallet_flutter/widgets/widgets.dart'; +import 'package:znn_sdk_dart/znn_sdk_dart.dart'; class NodeManagement extends StatefulWidget { final VoidCallback onNodeChangedCallback; @@ -32,17 +34,32 @@ class _NodeManagementState extends State { final GlobalKey _confirmNodeButtonKey = GlobalKey(); final GlobalKey _addNodeButtonKey = GlobalKey(); + final GlobalKey _confirmChainIdButtonKey = GlobalKey(); TextEditingController _newNodeController = TextEditingController(); GlobalKey _newNodeKey = GlobalKey(); + TextEditingController _newChainIdController = TextEditingController(); + GlobalKey _newChainIdKey = GlobalKey(); + late String _selectedNodeConfirmed; + late int _currentChainId; + + int get _newChainId => int.parse(_newChainIdController.text); @override void didChangeDependencies() { super.didChangeDependencies(); _selectedNode ??= kCurrentNode!; _selectedNodeConfirmed = _selectedNode!; + _initCurrentChainId(); + } + + void _initCurrentChainId() { + _currentChainId = sharedPrefsService!.get( + kChainIdKey, + defaultValue: kChainIdDefaultValue, + ); } @override @@ -61,6 +78,10 @@ class _NodeManagementState extends State { return ListView( shrinkWrap: true, children: [ + CustomExpandablePanel( + 'Chain id selection', + _getChainIdSelectionExpandableChild(), + ), CustomExpandablePanel( 'Node selection', _getNodeSelectionExpandableChild(), @@ -68,7 +89,7 @@ class _NodeManagementState extends State { CustomExpandablePanel( 'Add node', _getAddNodeExpandableChild(), - ) + ), ], ); } @@ -266,6 +287,7 @@ class _NodeManagementState extends State { @override void dispose() { _newNodeController.dispose(); + _newChainIdController.dispose(); super.dispose(); } @@ -279,4 +301,80 @@ class _NodeManagementState extends State { ), ); } + + Widget _getChainIdSelectionExpandableChild() { + return Column( + children: [ + Text( + 'Current chain id: $_currentChainId', + style: Theme.of(context).textTheme.subtitle1, + ), + kVerticalSpacing, + Form( + key: _newChainIdKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: InputField( + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + controller: _newChainIdController, + hintText: 'Node address with port', + onSubmitted: (value) { + if (_ifUserInputValid()) { + _onAddNodePressed(); + } + }, + onChanged: (String value) { + if (value.isNotEmpty) { + setState(() {}); + } + }, + validator: InputValidators.validateNumber, + ), + ), + kVerticalSpacing, + LoadingButton.settings( + onPressed: _isChainIdSelectionInputIsValid() + ? _onConfirmChainIdPressed + : null, + text: 'Confirm chain id', + key: _confirmChainIdButtonKey, + ), + ], + ); + } + + bool _isChainIdSelectionInputIsValid() => + InputValidators.validateNumber(_newChainIdController.text) == null && + _newChainId != _currentChainId; + + Future _onConfirmChainIdPressed() async { + try { + _confirmChainIdButtonKey.currentState?.animateForward(); + setChainIdentifier(chainIdentifier: _newChainId); + await sharedPrefsService!.put(kChainIdKey, _newChainId); + _sendSuccessfullyChangedChainIdNotification(_newChainId); + _initCurrentChainId(); + _newChainIdController = TextEditingController(); + _newChainIdKey = GlobalKey(); + } catch (e) { + NotificationUtils.sendNotificationError( + e, + 'Error while saving new chain id', + ); + } finally { + _confirmChainIdButtonKey.currentState?.animateReverse(); + } + } + + void _sendSuccessfullyChangedChainIdNotification(int newChainId) { + sl.get().addNotification( + WalletNotification( + title: 'Successfully changed chain id to: $newChainId', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: 'Successfully changed chain id from $_currentChainId to $_newChainId', + type: NotificationType.changedNode, + ), + ); + } } From c46a9bd83f835a8c3696ce3a598c6bbb4e8d2ae1 Mon Sep 17 00:00:00 2001 From: mik3mast3rs Date: Sat, 28 Jan 2023 17:11:12 +0200 Subject: [PATCH 4/4] Feat: Show warning dialog when user changes node and the node chain id is different than the current one --- .../settings_widgets/node_management.dart | 32 +++++++++++++++---- lib/widgets/reusable_widgets/dialogs.dart | 6 ++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/lib/widgets/modular_widgets/settings_widgets/node_management.dart b/lib/widgets/modular_widgets/settings_widgets/node_management.dart index fac09893..8e41ce5d 100644 --- a/lib/widgets/modular_widgets/settings_widgets/node_management.dart +++ b/lib/widgets/modular_widgets/settings_widgets/node_management.dart @@ -155,6 +155,7 @@ class _NodeManagementState extends State { _selectedNode, ); kCurrentNode = _selectedNode!; + await _checkForChainIdDifferences(); _sendChangingNodeSuccessNotification(); widget.onNodeChangedCallback(); } else { @@ -369,12 +370,31 @@ class _NodeManagementState extends State { void _sendSuccessfullyChangedChainIdNotification(int newChainId) { sl.get().addNotification( - WalletNotification( - title: 'Successfully changed chain id to: $newChainId', - timestamp: DateTime.now().millisecondsSinceEpoch, - details: 'Successfully changed chain id from $_currentChainId to $_newChainId', - type: NotificationType.changedNode, - ), + WalletNotification( + title: 'Successfully changed chain id to: $newChainId', + timestamp: DateTime.now().millisecondsSinceEpoch, + details: + 'Successfully changed chain id from $_currentChainId to $_newChainId', + type: NotificationType.changedNode, + ), + ); + } + + Future _checkForChainIdDifferences() async { + await zenon!.ledger.getFrontierMomentum().then((momentum) { + int nodeChainId = momentum.chainIdentifier; + if (nodeChainId != _currentChainId) { + _showDifferentChainIdDialog(nodeChainId, _currentChainId); + } + }); + } + + void _showDifferentChainIdDialog(int nodeChainId, int currentChainId) { + showOkDialog( + context: context, + title: 'Different chain id', + description: 'The new node: $_selectedNode has a different ' + 'chain id ($nodeChainId) than the current one ($currentChainId)', ); } } diff --git a/lib/widgets/reusable_widgets/dialogs.dart b/lib/widgets/reusable_widgets/dialogs.dart index ea57a79d..d43ac0b2 100644 --- a/lib/widgets/reusable_widgets/dialogs.dart +++ b/lib/widgets/reusable_widgets/dialogs.dart @@ -5,7 +5,7 @@ showOkDialog({ required BuildContext context, required String title, required String description, - required VoidCallback onActionButtonPressed, + VoidCallback? onActionButtonPressed, }) => showDialog( context: context, @@ -14,7 +14,9 @@ showOkDialog({ content: Text(description), actions: [ TextButton( - onPressed: onActionButtonPressed, + onPressed: onActionButtonPressed ?? () { + Navigator.pop(context); + }, child: Text( 'OK', style: Theme.of(context).textTheme.bodyText1,