From fc0fde372749a837fb919c6f9b0a2a1045cc523c Mon Sep 17 00:00:00 2001 From: SteveJosephh21 Date: Fri, 25 Aug 2023 17:56:27 +0530 Subject: [PATCH 1/4] Image added in splash screen --- android/app/src/main/AndroidManifest.xml | 9 +++++++++ .../src/main/res/drawable-v24/launch_background.xml | 11 +++++++++++ .../app/src/main/res/drawable/launch_background.xml | 13 ++++++------- android/app/src/main/res/values/styles.xml | 2 +- 4 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 android/app/src/main/res/drawable-v24/launch_background.xml diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c5e594b..bc23f7e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -28,6 +28,15 @@ android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" /> + + diff --git a/android/app/src/main/res/drawable-v24/launch_background.xml b/android/app/src/main/res/drawable-v24/launch_background.xml new file mode 100644 index 0000000..27c44be --- /dev/null +++ b/android/app/src/main/res/drawable-v24/launch_background.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml index fc58c18..27c44be 100644 --- a/android/app/src/main/res/drawable/launch_background.xml +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -1,12 +1,11 @@ - - + - + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 1f83a33..98e5d04 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -13,6 +13,6 @@ This Theme is only used starting with V2 of Flutter's Android embedding. --> From 45d27ed624702cb31d01b0d3d8273b2e0f09df2c Mon Sep 17 00:00:00 2001 From: SteveJosephh21 Date: Fri, 25 Aug 2023 17:57:59 +0530 Subject: [PATCH 2/4] Updated SDK version 33 --- android/app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index ac10ce0..523a291 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,7 +32,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 33 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -49,8 +49,8 @@ android { defaultConfig { applicationId "io.beldex.master_node_monitor" - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion + minSdkVersion 23 + targetSdkVersion 33 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } From 19156c9fcc320f4993f82b57254e4de094a834da Mon Sep 17 00:00:00 2001 From: SteveJosephh21 Date: Fri, 25 Aug 2023 18:00:17 +0530 Subject: [PATCH 3/4] Added input field validation and Check the status of internet connectivity in various screens --- lib/generated/intl/messages_de.dart | 5 + lib/generated/intl/messages_en.dart | 5 + lib/generated/l10n.dart | 50 +++ lib/l10n/intl_de.arb | 7 +- lib/l10n/intl_en.arb | 7 +- lib/main.dart | 4 + lib/src/beldex/check_master_node.dart | 51 +++ lib/src/screens/add_new_daemon_page.dart | 73 +++- lib/src/screens/add_new_master_node_page.dart | 131 +++++-- lib/src/screens/dashboard_page.dart | 359 ++++++++++++------ lib/src/screens/edit_master_node_page.dart | 22 +- lib/src/utils/default_settings_migration.dart | 4 +- lib/src/utils/network_service.dart | 19 + pubspec.lock | 56 +++ pubspec.yaml | 1 + 15 files changed, 619 insertions(+), 175 deletions(-) create mode 100644 lib/src/beldex/check_master_node.dart create mode 100644 lib/src/utils/network_service.dart diff --git a/lib/generated/intl/messages_de.dart b/lib/generated/intl/messages_de.dart index f14b838..fb93d24 100644 --- a/lib/generated/intl/messages_de.dart +++ b/lib/generated/intl/messages_de.dart @@ -43,6 +43,7 @@ class MessageLookup extends MessageLookupByLibrary { "amount" : MessageLookupByLibrary.simpleMessage("Betrag"), "awaiting_contributions" : MessageLookupByLibrary.simpleMessage("Warten auf Beiträge"), "blocks" : MessageLookupByLibrary.simpleMessage("Blöcke"), + "checkYourInternetConnection" : MessageLookupByLibrary.simpleMessage("Check your internet connection"), "checkpoints" : MessageLookupByLibrary.simpleMessage("Checkpoint Blöcke"), "contributors" : MessageLookupByLibrary.simpleMessage("Beiträger"), "copied_to_clipboard" : m1, @@ -52,6 +53,8 @@ class MessageLookup extends MessageLookupByLibrary { "dashboard_order_by_next_reward" : MessageLookupByLibrary.simpleMessage("Nächste Belohnung"), "delete_master_node" : MessageLookupByLibrary.simpleMessage("Master Node löschen"), "earned_downtime_blocks" : MessageLookupByLibrary.simpleMessage("Verdiente Downtime Blöcke"), + "enterAPublicKey" : MessageLookupByLibrary.simpleMessage("Enter a public key"), + "enterAValidPublicKey" : MessageLookupByLibrary.simpleMessage("Enter a valid public key"), "error_name_taken" : MessageLookupByLibrary.simpleMessage("Du hast bereits einen Node mit dem Namen"), "error_public_key_too_long" : MessageLookupByLibrary.simpleMessage("Öffentlicher Schlüssel zu lang"), "error_public_key_too_short" : MessageLookupByLibrary.simpleMessage("Öffentlicher Schlüssel zu kurz"), @@ -88,6 +91,8 @@ class MessageLookup extends MessageLookupByLibrary { "name" : MessageLookupByLibrary.simpleMessage("Name"), "next_reward" : MessageLookupByLibrary.simpleMessage("Nächste Belohnung:"), "ok" : MessageLookupByLibrary.simpleMessage("OK"), + "pleaseEnterAName" : MessageLookupByLibrary.simpleMessage("Please enter a name"), + "pleaseEnterAValidDaemon" : MessageLookupByLibrary.simpleMessage("Please enter a valid daemon"), "please_select" : MessageLookupByLibrary.simpleMessage("Bitte auswählen:"), "pos" : MessageLookupByLibrary.simpleMessage("POS Blöcke"), "public_ip" : MessageLookupByLibrary.simpleMessage("IP Adresse"), diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 61625a5..97c16e8 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -45,6 +45,7 @@ class MessageLookup extends MessageLookupByLibrary { "amount" : MessageLookupByLibrary.simpleMessage("Amount"), "awaiting_contributions" : MessageLookupByLibrary.simpleMessage("Awaiting Contributions"), "blocks" : MessageLookupByLibrary.simpleMessage("blocks"), + "checkYourInternetConnection" : MessageLookupByLibrary.simpleMessage("Check your internet connection"), "checkpoints" : MessageLookupByLibrary.simpleMessage("Checkpoints"), "contributors" : MessageLookupByLibrary.simpleMessage("Contributors"), "copied_to_clipboard" : m1, @@ -54,6 +55,8 @@ class MessageLookup extends MessageLookupByLibrary { "dashboard_order_by_next_reward" : MessageLookupByLibrary.simpleMessage("Next Reward"), "delete_master_node" : MessageLookupByLibrary.simpleMessage("Delete Master Node"), "earned_downtime_blocks" : MessageLookupByLibrary.simpleMessage("Earned Downtime Blocks"), + "enterAPublicKey" : MessageLookupByLibrary.simpleMessage("Enter a public key"), + "enterAValidPublicKey" : MessageLookupByLibrary.simpleMessage("Enter a valid public key"), "error_name_taken" : MessageLookupByLibrary.simpleMessage("You already have a node with that name"), "error_public_key_too_long" : MessageLookupByLibrary.simpleMessage("Public Key too long"), "error_public_key_too_short" : MessageLookupByLibrary.simpleMessage("Public Key too short"), @@ -91,6 +94,8 @@ class MessageLookup extends MessageLookupByLibrary { "name" : MessageLookupByLibrary.simpleMessage("Name"), "next_reward" : MessageLookupByLibrary.simpleMessage("Next Reward:"), "ok" : MessageLookupByLibrary.simpleMessage("OK"), + "pleaseEnterAName" : MessageLookupByLibrary.simpleMessage("Please enter a name"), + "pleaseEnterAValidDaemon" : MessageLookupByLibrary.simpleMessage("Please enter a valid daemon"), "please_select" : MessageLookupByLibrary.simpleMessage("Please select:"), "pos" : MessageLookupByLibrary.simpleMessage("POS"), "public_ip" : MessageLookupByLibrary.simpleMessage("IP Address"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 3235b1b..d1bce08 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -934,6 +934,56 @@ class S { args: [], ); } + + /// `Please enter a name` + String get pleaseEnterAName { + return Intl.message( + 'Please enter a name', + name: 'pleaseEnterAName', + desc: '', + args: [], + ); + } + + /// `Enter a public key` + String get enterAPublicKey { + return Intl.message( + 'Enter a public key', + name: 'enterAPublicKey', + desc: '', + args: [], + ); + } + + /// `Enter a valid public key` + String get enterAValidPublicKey { + return Intl.message( + 'Enter a valid public key', + name: 'enterAValidPublicKey', + desc: '', + args: [], + ); + } + + /// `Please enter a valid daemon` + String get pleaseEnterAValidDaemon { + return Intl.message( + 'Please enter a valid daemon', + name: 'pleaseEnterAValidDaemon', + desc: '', + args: [], + ); + } + + /// `Check your internet connection` + String get checkYourInternetConnection { + return Intl.message( + 'Check your internet connection', + name: 'checkYourInternetConnection', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 599fe38..7c9c9bb 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -97,5 +97,10 @@ "title_faq": "FAQ", "termsConditions": "Terms & amp; Bedingungen", "help": "Help", - "termsAndConditions": "Terms and Conditions" + "termsAndConditions": "Terms and Conditions", + "pleaseEnterAName": "Please enter a name", + "enterAPublicKey": "Enter a public key", + "enterAValidPublicKey": "Enter a valid public key", + "pleaseEnterAValidDaemon": "Please enter a valid daemon", + "checkYourInternetConnection": "Check your internet connection" } diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index f96e77f..cf52b05 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -99,5 +99,10 @@ "title_faq": "FAQ", "termsConditions": "Terms & Conditions", "help": "Help", - "termsAndConditions": "Terms and Conditions" + "termsAndConditions": "Terms and Conditions", + "pleaseEnterAName": "Please enter a name", + "enterAPublicKey": "Enter a public key", + "enterAValidPublicKey": "Enter a valid public key", + "pleaseEnterAValidDaemon": "Please enter a valid daemon", + "checkYourInternetConnection": "Check your internet connection" } diff --git a/lib/main.dart b/lib/main.dart index 4f4081d..b58ec8c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,6 +10,7 @@ import 'package:master_node_monitor/src/stores/node_sync_store.dart'; import 'package:master_node_monitor/src/stores/settings_store.dart'; import 'package:master_node_monitor/src/utils/default_settings_migration.dart'; import 'package:master_node_monitor/src/utils/language.dart'; +import 'package:master_node_monitor/src/utils/network_service.dart'; import 'package:master_node_monitor/src/utils/router/beldex_router.dart'; import 'package:master_node_monitor/src/utils/theme/palette.dart'; import 'package:master_node_monitor/src/utils/theme/theme_changer.dart'; @@ -39,6 +40,8 @@ Future main() async { await SettingsStoreBase.load(sharedPreferences, daemons); final nodeSyncStore = NodeSyncStore(masterNodes, settingsStore); + final networkService = NetworkService().controller.stream; + if (masterNodes.isNotEmpty) { await nodeSyncStore.sync(); nodeSyncStore.startSync(); @@ -50,6 +53,7 @@ Future main() async { Provider(create: (_) => sharedPreferences), Provider(create: (_) => settingsStore), Provider(create: (_) => nodeSyncStore), + StreamProvider(create: (_) => networkService) ], child: BeldexMasterNodeApp())); } diff --git a/lib/src/beldex/check_master_node.dart b/lib/src/beldex/check_master_node.dart new file mode 100644 index 0000000..2c29850 --- /dev/null +++ b/lib/src/beldex/check_master_node.dart @@ -0,0 +1,51 @@ +import 'dart:convert'; +import 'dart:ffi'; +import 'package:hive/hive.dart'; +import 'package:http/http.dart' as http; + +@HiveType(typeId: 1) +class CheckMasterNode extends HiveObject { + CheckMasterNode(this.uri,this.publicKey); + + CheckMasterNode.fromMap(Map map) : uri = (map['uri'] ?? '') as String,publicKey = (map['publicKey'] ?? '') as String; + + static const boxName = 'CheckMasterNodes'; + + @HiveField(0) + String uri; + + @HiveField(1) + String publicKey; + + Future isOnline() async { + try { + final resBody = await sendRPCRequest('get_master_nodes',{'master_node_pubkeys':[publicKey]}); + if(resBody['result']['master_node_states']!=null){ + return true; + }else{ + return false; + } + } catch (e) { + return false; + } + } + + Future> sendRPCRequest(String method, Map params) async { + Map resultBody; + + final requestBody = params != null + ? {'jsonrpc': '2.0', 'id': '0', 'method': method, 'params': params} + : {'jsonrpc': '2.0', 'id': '0', 'method': method}; + + final url = Uri.http(uri, '/json_rpc'); + final headers = {'Content-type': 'application/json'}; + final body = json.encode(requestBody); + final response = await http.post(url, headers: headers, body: body); + final newBody = response.body.replaceAllMapped( + RegExp(r'("swarm_id":)(\d+)'), + (match) => '${match.group(1)}"${match.group(2)}"'); + + resultBody = json.decode(newBody) as Map; + return resultBody; + } +} \ No newline at end of file diff --git a/lib/src/screens/add_new_daemon_page.dart b/lib/src/screens/add_new_daemon_page.dart index fcd8838..b31342b 100644 --- a/lib/src/screens/add_new_daemon_page.dart +++ b/lib/src/screens/add_new_daemon_page.dart @@ -2,7 +2,9 @@ import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:master_node_monitor/generated/l10n.dart'; import 'package:master_node_monitor/src/beldex/daemon.dart'; +import 'package:master_node_monitor/src/utils/network_service.dart'; import 'package:master_node_monitor/src/utils/router/beldex_routes.dart'; +import 'package:master_node_monitor/src/utils/theme/palette.dart'; import 'package:master_node_monitor/src/widgets/base_page.dart'; import 'package:master_node_monitor/src/widgets/beldex/beldex_text_field.dart'; import 'package:master_node_monitor/src/widgets/primary_button.dart'; @@ -25,6 +27,7 @@ class AddNewDaemonPageBodyState extends State { final _hostController = TextEditingController(); final _portController = TextEditingController(); final _formKey = GlobalKey(); + bool isLoading = false; @override void dispose() { @@ -33,21 +36,55 @@ class AddNewDaemonPageBodyState extends State { super.dispose(); } - Future _saveDaemon(Box daemonSource) async { - var uri = _hostController.text; - final port = _portController.text; + Future _saveDaemon(Box daemonSource, NetworkStatus networkStatus) async { + if(networkStatus == NetworkStatus.online) { + var uri = _hostController.text; + final port = _portController.text; - if (port != null && port.isNotEmpty) uri = '$uri:$port'; + if (port != null && port.isNotEmpty) uri = '$uri:$port'; + final daemon = Daemon(uri); + bool daemonIsOnline = await daemon.isOnline(); + if (daemonIsOnline) { + await daemonSource.add(daemon); + Navigator.of(context).pop(); + Navigator.of(context).pushNamed(BeldexRoutes.settingsDaemon); + } else { + callCommonScaffoldMessenger(S.of(context).pleaseEnterAValidDaemon); + setLoading(false); + } + }else{ + callCommonScaffoldMessenger(S.of(context).checkYourInternetConnection); + setLoading(false); + } + } - final daemon = Daemon(uri); - await daemonSource.add(daemon); + void callCommonScaffoldMessenger(String text){ + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + content: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(text, + style: TextStyle(fontSize: 16, color: Colors.white),), + ], + ), + behavior: SnackBarBehavior.floating, + backgroundColor: BeldexPalette.red)); } String _validateNodeAddress(String value) { const pattern = '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\$|^[-0-9a-zA-Z.]{1,253}\$'; final isValid = RegExp(pattern).hasMatch(value); - return isValid ? null : S.current.error_text_daemon_address; + if(isValid) { + return null; + }else { + setLoading(false); + return S.current.error_text_daemon_address; + } } String _validateNodePort(String value) { @@ -62,7 +99,12 @@ class AddNewDaemonPageBodyState extends State { } catch (e) {} } - return isValid ? null : S.current.error_text_daemon_port; + if(isValid) { + return null; + }else{ + setLoading(false); + return S.current.error_text_daemon_port; + } } Future _onBackPressed() async { @@ -71,9 +113,16 @@ class AddNewDaemonPageBodyState extends State { return null; } + void setLoading(bool value) { + setState(() { + isLoading = value; + }); + } + @override Widget build(BuildContext context) { final daemonSource = Provider.of>(context); + final networkStatus = Provider.of(context); return WillPopScope( onWillPop: _onBackPressed, @@ -142,12 +191,12 @@ class AddNewDaemonPageBodyState extends State { ), Container( margin: EdgeInsets.only(left: 20, right: 20, top: 0, bottom: 30), - child: PrimaryButton( + child: LoadingPrimaryButton( + isLoading: isLoading, onPressed: () async { + setLoading(true); if (!_formKey.currentState.validate()) return; - await _saveDaemon(daemonSource); - Navigator.of(context).pop(); - Navigator.of(context).pushNamed(BeldexRoutes.settingsDaemon); + await _saveDaemon(daemonSource,networkStatus); }, text: S.of(context).add_daemon, color: Theme.of(context).primaryTextTheme.button.backgroundColor, diff --git a/lib/src/screens/add_new_master_node_page.dart b/lib/src/screens/add_new_master_node_page.dart index 8e6a3e3..4d888f6 100644 --- a/lib/src/screens/add_new_master_node_page.dart +++ b/lib/src/screens/add_new_master_node_page.dart @@ -2,8 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; import 'package:master_node_monitor/generated/l10n.dart'; +import 'package:master_node_monitor/src/beldex/check_master_node.dart'; import 'package:master_node_monitor/src/beldex/master_node.dart'; import 'package:master_node_monitor/src/stores/node_sync_store.dart'; +import 'package:master_node_monitor/src/stores/settings_store.dart'; +import 'package:master_node_monitor/src/utils/network_service.dart'; import 'package:master_node_monitor/src/utils/router/beldex_routes.dart'; import 'package:master_node_monitor/src/utils/theme/palette.dart'; import 'package:master_node_monitor/src/utils/validators.dart'; @@ -37,6 +40,7 @@ class AddNewMasterNodePageBodyState extends State { final _nameController = TextEditingController(); final _publicKeyController = TextEditingController(); final _formKey = GlobalKey(); + bool isLoading = false; AddNewMasterNodePageBodyState(this.status); final bool status; @@ -56,16 +60,75 @@ class AddNewMasterNodePageBodyState extends State { } - Future _saveMasterNode(Box masterNodeSource) async { - final masterNode = - MasterNode(_nameController.text, _publicKeyController.text); - await masterNodeSource.add(masterNode); + Future _saveMasterNode(Box masterNodeSource, SettingsStore settingsStore, NodeSyncStore nodeSyncStatus, NetworkStatus networkStatus) async { + if(networkStatus == NetworkStatus.online) { + var checkPublicKey = settingsStore.daemon != null + ? CheckMasterNode(settingsStore.daemon.uri, _publicKeyController.text) + : CheckMasterNode("", _publicKeyController.text); + bool validPublicKey = await checkPublicKey.isOnline(); + if (validPublicKey) { + final masterNode = MasterNode(_nameController.text, _publicKeyController.text); + await masterNodeSource.add(masterNode); + await nodeSyncStatus.sync(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + content: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.check_circle_rounded, color: Colors.white,), + SizedBox(width: 10,), + Text(S + .of(context) + .success_saved_node, + style: TextStyle(fontSize: 16, color: Colors.white),), + ], + ), + behavior: SnackBarBehavior.floating, + backgroundColor: BeldexPalette.tealWithOpacity)); + Navigator.of(context).pop(); + Navigator.pushNamed(context, BeldexRoutes.dashboard); + } else { + callCommonScaffoldMessenger(S.of(context).enterAValidPublicKey); + setLoading(false); + } + }else{ + callCommonScaffoldMessenger(S.of(context).checkYourInternetConnection); + setLoading(false); + } + } + + void callCommonScaffoldMessenger(String text){ + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + content: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(text, + style: TextStyle(fontSize: 16, color: Colors.white),), + ], + ), + behavior: SnackBarBehavior.floating, + backgroundColor: BeldexPalette.red)); + } + + void setLoading(bool value) { + setState(() { + isLoading = value; + }); } @override Widget build(BuildContext context) { final masterNodeSource = context.read>(); final nodeSyncStatus = context.read(); + final settingsStore = Provider.of(context); + final networkStatus = Provider.of(context); return Container( color: Theme.of(context).backgroundColor, @@ -120,7 +183,16 @@ class AddNewMasterNodePageBodyState extends State { validator: (value) { final isDuplicate = _isDuplicateName(value, masterNodeSource); - if (isDuplicate) return S.of(context).error_name_taken; + if (value.isEmpty) { + setLoading(false); + return S.of(context).pleaseEnterAName; + } + else if (isDuplicate) { + setLoading(false); + return S + .of(context) + .error_name_taken; + } return null; }, ), @@ -142,15 +214,24 @@ class AddNewMasterNodePageBodyState extends State { _publicKeyController.text = clipboard.text; }), validator: (value) { - final validPublicKey = isValidPublicKey(value); + final publicKey = value.trim(); + final validPublicKey = isValidPublicKey(publicKey); final isDuplicate = - _isDuplicatePublicKey(value, masterNodeSource); - if (value.isEmpty || validPublicKey == KeyValidity.TOO_SHORT) - return S.of(context).error_public_key_too_short; - else if (validPublicKey == KeyValidity.TOO_LONG) - return S.of(context).error_public_key_too_long; - else if (isDuplicate) - return S.of(context).error_you_are_already_monitoring; + _isDuplicatePublicKey(publicKey, masterNodeSource); + if (publicKey.isEmpty) { + setLoading(false); + return S.of(context).enterAPublicKey; + } + else if(validPublicKey == KeyValidity.TOO_SHORT || validPublicKey == KeyValidity.TOO_LONG) { + setLoading(false); + return S.of(context).enterAValidPublicKey; + } + else if (isDuplicate) { + setLoading(false); + return S + .of(context) + .error_you_are_already_monitoring; + } return null; }, ), @@ -160,28 +241,12 @@ class AddNewMasterNodePageBodyState extends State { ), Container( margin: EdgeInsets.only(left: 20, right: 20, top: 0, bottom: 30), - child: PrimaryButton( + child: LoadingPrimaryButton( + isLoading: isLoading, onPressed: () async { + setLoading(true); if (!_formKey.currentState.validate()) return; - await _saveMasterNode(masterNodeSource); - await nodeSyncStatus.sync(); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - content: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon(Icons.check_circle_rounded,color: Colors.white,), - SizedBox(width: 10,), - Text(S.of(context).success_saved_node,style: TextStyle(fontSize:16,color: Colors.white),), - ], - ), - behavior: SnackBarBehavior.floating, - backgroundColor: BeldexPalette.tealWithOpacity)); - Navigator.of(context).pop(); - Navigator.pushNamed(context, BeldexRoutes.dashboard); + await _saveMasterNode(masterNodeSource,settingsStore,nodeSyncStatus,networkStatus); }, text: S.of(context).add_master_node, color: Theme.of(context).primaryTextTheme.button.backgroundColor, diff --git a/lib/src/screens/dashboard_page.dart b/lib/src/screens/dashboard_page.dart index 62a4bae..71d7b9e 100644 --- a/lib/src/screens/dashboard_page.dart +++ b/lib/src/screens/dashboard_page.dart @@ -4,11 +4,13 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:hive/hive.dart'; import 'package:master_node_monitor/generated/l10n.dart'; +import 'package:master_node_monitor/src/beldex/check_master_node.dart'; import 'package:master_node_monitor/src/beldex/master_node.dart'; import 'package:master_node_monitor/src/beldex/master_node_status.dart'; import 'package:master_node_monitor/src/stores/node_sync_store.dart'; import 'package:master_node_monitor/src/stores/settings_store.dart'; import 'package:master_node_monitor/src/utils/dashboard_sort_order.dart'; +import 'package:master_node_monitor/src/utils/network_service.dart'; import 'package:master_node_monitor/src/utils/router/beldex_routes.dart'; import 'package:master_node_monitor/src/utils/theme/palette.dart'; import 'package:master_node_monitor/src/utils/theme/theme_changer.dart'; @@ -108,10 +110,66 @@ class DashboardPageBodyState extends State { bool _isDuplicateName(String name, Box masterNodeSource) => masterNodeSource.values.any((element) => element.name == name); - Future _saveMasterNode(Box masterNodeSource) async { - final masterNode = - MasterNode(_nameController.text, _publicKeyController.text); - await masterNodeSource.add(masterNode); + Future _saveMasterNode(Box masterNodeSource, SettingsStore settingsStore, NodeSyncStore nodeSyncStatus, NetworkStatus networkStatus) async { + if(networkStatus == NetworkStatus.online) { + var checkPublicKey = settingsStore.daemon != null + ? CheckMasterNode(settingsStore.daemon.uri, _publicKeyController.text) + : CheckMasterNode("", _publicKeyController.text); + bool validPublicKey = await checkPublicKey.isOnline(); + if (validPublicKey) { + final masterNode = MasterNode( + _nameController.text, _publicKeyController.text); + await masterNodeSource.add(masterNode); + await nodeSyncStatus.sync(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + content: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.check_circle_rounded, + color: Colors.white,), + SizedBox(width: 10,), + Text(S + .of(context) + .success_saved_node, style: TextStyle( + fontSize: 16, color: Colors.white),), + ], + ), + behavior: SnackBarBehavior.floating, + backgroundColor: BeldexPalette + .tealWithOpacity)); + Navigator.of(context).pop(); + _nameController.text = ""; + _publicKeyController.text = ""; + await nodeSyncStatus.sync(); + }else { + callCommonScaffoldMessenger(S.of(context).enterAValidPublicKey); + setLoading(false); + } + }else{ + callCommonScaffoldMessenger(S.of(context).checkYourInternetConnection); + setLoading(false); + } + } + + void callCommonScaffoldMessenger(String text){ + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + content: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(text, + style: TextStyle(fontSize: 16, color: Colors.white),), + ], + ), + behavior: SnackBarBehavior.floating, + backgroundColor: BeldexPalette.red)); } @override @@ -120,134 +178,187 @@ class DashboardPageBodyState extends State { _publicKeyController.dispose(); super.dispose(); } + StateSetter _setState; + bool isLoading = false; + + void setLoading(bool value) { + _setState(() { + isLoading = value; + }); + } //SteveJosephh21 - Future showDialogBox(BuildContext context, TextEditingController _nameController, Box masterNodeSource, NodeSyncStore nodeSyncStatus, TextEditingController _publicKeyController, GlobalKey _formKey){ + Future showDialogBox(BuildContext context, TextEditingController _nameController, Box masterNodeSource, NodeSyncStore nodeSyncStatus, TextEditingController _publicKeyController, GlobalKey _formKey, SettingsStore settingsStore){ return showDialog( context:context, barrierDismissible: false, - builder:(context)=> Center( - child: Card( - elevation: 10, - color: Theme.of(context).cardColor, - margin: EdgeInsets.fromLTRB(10,0,10,0), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10) - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Container( - margin: EdgeInsets.only(left: 20, right: 20, top: 30, bottom: 0), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - Text(S.current.title_add_master_node, - style: TextStyle( - fontSize: 25, - fontWeight:FontWeight.bold - ),), - SizedBox(width:50), - InkWell(onTap:(){ - Navigator.of(context).pop(); - _nameController.text=""; - _publicKeyController.text=""; - },child: SvgPicture.asset('assets/images/close.svg',width: 25,height: 25,color: Theme.of(context).primaryTextTheme.caption.color,)), - ], - ), + builder:(context)=> StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + final networkStatus = Provider.of(context); + _setState = setState; + return Center( + child: Card( + elevation: 10, + color: Theme + .of(context) + .cardColor, + margin: EdgeInsets.fromLTRB(10, 0, 10, 0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10) ), - Form( - key: _formKey, - child: Container( - padding: EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 30), - child: Column(children: [ - Padding( - padding: EdgeInsets.only(top: 20), - child: BeldexTextField( - backgroundColor: Theme.of(context).primaryTextTheme.overline.color, - controller: _nameController, - hintText: S.of(context).name, - validator: (value) { - final isDuplicate = - _isDuplicateName(value, masterNodeSource); - if (isDuplicate) return S.of(context).error_name_taken; - return null; - }, - ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: EdgeInsets.only( + left: 20, right: 20, top: 30, bottom: 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Text(S.current.title_add_master_node, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold + ),), + SizedBox(width: 50), + InkWell(onTap: () { + Navigator.of(context).pop(); + _nameController.text = ""; + _publicKeyController.text = ""; + }, child: SvgPicture.asset( + 'assets/images/close.svg', width: 25, + height: 25, + color: Theme + .of(context) + .primaryTextTheme + .caption + .color,)), + ], ), - Padding( - padding: EdgeInsets.only(top: 20), - child: BeldexTextField( - backgroundColor: Theme.of(context).primaryTextTheme.overline.color, - controller: _publicKeyController, - hintText: S.of(context).public_key, - suffixIcon: IconButton( - splashColor: Colors.transparent, - highlightColor: Colors.transparent, - color: BeldexPalette.pasteIcon, - icon: Icon(Icons.content_paste_sharp), - onPressed: () async { - final clipboard = await Clipboard.getData('text/plain'); - if (clipboard.text != null) - _publicKeyController.text = clipboard.text; - }), - validator: (value) { - final validPublicKey = isValidPublicKey(value); - final isDuplicate = - _isDuplicatePublicKey(value, masterNodeSource); - if (value.isEmpty || validPublicKey == KeyValidity.TOO_SHORT) - return S.of(context).error_public_key_too_short; - else if (validPublicKey == KeyValidity.TOO_LONG) - return S.of(context).error_public_key_too_long; - else if (isDuplicate) - return S.of(context).error_you_are_already_monitoring; - return null; - }, - ), - ), - ]), - ), - ), - Container( - margin: EdgeInsets.only(left: 20, right: 20, top: 0, bottom: 30), - child: PrimaryButton( - onPressed: () async { - if (!_formKey.currentState.validate()) return; - await _saveMasterNode(masterNodeSource); - await nodeSyncStatus.sync(); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + ), + Form( + key: _formKey, + child: Container( + padding: EdgeInsets.only( + left: 20, right: 20, top: 10, bottom: 30), + child: Column(children: [ + Padding( + padding: EdgeInsets.only(top: 20), + child: BeldexTextField( + backgroundColor: Theme + .of(context) + .primaryTextTheme + .overline + .color, + controller: _nameController, + hintText: S + .of(context) + .name, + validator: (value) { + final isDuplicate = + _isDuplicateName(value, masterNodeSource); + if (value.isEmpty) { + setLoading(false); + return S.of(context).pleaseEnterAName; + } + else if (isDuplicate) { + setLoading(false); + return S + .of(context) + .error_name_taken; + } + return null; + }, + ), ), - content: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon(Icons.check_circle_rounded,color: Colors.white,), - SizedBox(width: 10,), - Text(S.of(context).success_saved_node,style: TextStyle(fontSize:16,color: Colors.white),), - ], + Padding( + padding: EdgeInsets.only(top: 20), + child: BeldexTextField( + backgroundColor: Theme + .of(context) + .primaryTextTheme + .overline + .color, + controller: _publicKeyController, + hintText: S + .of(context) + .public_key, + suffixIcon: IconButton( + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + color: BeldexPalette.pasteIcon, + icon: Icon(Icons.content_paste_sharp), + onPressed: () async { + final clipboard = await Clipboard.getData( + 'text/plain'); + if (clipboard.text != null) + _publicKeyController.text = + clipboard.text; + }), + validator: (value) { + final publicKey = value.trim(); + final validPublicKey = isValidPublicKey( + publicKey); + final isDuplicate = + _isDuplicatePublicKey( + publicKey, masterNodeSource); + if (publicKey.isEmpty) { + setLoading(false); + return S.of(context).enterAPublicKey; + } + else + if (validPublicKey == KeyValidity.TOO_SHORT || + validPublicKey == KeyValidity.TOO_LONG) { + setLoading(false); + return S.of(context).enterAValidPublicKey; + } + else if (isDuplicate) { + setLoading(false); + return S + .of(context) + .error_you_are_already_monitoring; + } + return null; + }, ), - behavior: SnackBarBehavior.floating, - backgroundColor: BeldexPalette.tealWithOpacity)); - Navigator.of(context).pop(); - _nameController.text=""; - _publicKeyController.text=""; - await nodeSyncStatus.sync(); - //Navigator.pushNamed(context, BeldexRoutes.dashboard); - }, - text: S.of(context).add_master_node, - color: Theme.of(context).primaryTextTheme.button.backgroundColor, - borderColor: - Theme.of(context).primaryTextTheme.button.decorationColor), - ) - ], - ), - ), + ), + ]), + ), + ), + Container( + margin: EdgeInsets.only( + left: 20, right: 20, top: 0, bottom: 30), + child: LoadingPrimaryButton( + isLoading: isLoading, + onPressed: () async { + setLoading(true); + if (!_formKey.currentState.validate()) return; + await _saveMasterNode(masterNodeSource,settingsStore,nodeSyncStatus,networkStatus); + }, + text: S + .of(context) + .add_master_node, + color: Theme + .of(context) + .primaryTextTheme + .button + .backgroundColor, + borderColor: + Theme + .of(context) + .primaryTextTheme + .button + .decorationColor), + ) + ], + ), + ), + ); + } ) ); } @@ -420,7 +531,7 @@ class DashboardPageBodyState extends State { ), ), InkWell(onTap:(){ - showDialogBox(context,_nameController,masterNodeSource,nodeSyncStatus,_publicKeyController,_formKey); + showDialogBox(context,_nameController,masterNodeSource,nodeSyncStatus,_publicKeyController,_formKey,settingsStore); },child: Icon(Icons.add_circle)) ], ), diff --git a/lib/src/screens/edit_master_node_page.dart b/lib/src/screens/edit_master_node_page.dart index 16ca2e0..5d2531e 100644 --- a/lib/src/screens/edit_master_node_page.dart +++ b/lib/src/screens/edit_master_node_page.dart @@ -49,6 +49,7 @@ class EditMasterNodePageBodyState extends State { final bool status; final _nameController = TextEditingController(); final _formKey = GlobalKey(); + bool isLoading = false; Box masterNodeSource; MasterNode node; @@ -94,6 +95,12 @@ class EditMasterNodePageBodyState extends State { } } + void setLoading(bool value) { + setState(() { + isLoading = value; + }); + } + void showConfirmationDialog(BuildContext context, bool status, NodeSyncStore nodeSyncStore){ showDialog( context: context, @@ -199,7 +206,16 @@ class EditMasterNodePageBodyState extends State { hintText: S.of(context).name, validator: (value) { final isDuplicate = _isDuplicateName(value); - if (isDuplicate) return S.of(context).error_name_taken; + if (value.isEmpty) { + setLoading(false); + return S.of(context).pleaseEnterAName; + } + else if (isDuplicate) { + setLoading(false); + return S + .of(context) + .error_name_taken; + } return null; }, ), @@ -234,8 +250,10 @@ class EditMasterNodePageBodyState extends State { ]), ), ), - bottomSection: PrimaryButton( + bottomSection: LoadingPrimaryButton( + isLoading:isLoading, onPressed: () async { + setLoading(true); if (!_formKey.currentState.validate()) return; await _saveMasterNode(); await nodeSyncStore.sync(); diff --git a/lib/src/utils/default_settings_migration.dart b/lib/src/utils/default_settings_migration.dart index e93363f..6c949e8 100644 --- a/lib/src/utils/default_settings_migration.dart +++ b/lib/src/utils/default_settings_migration.dart @@ -49,10 +49,10 @@ Future changeCurrentNodeToDefault( if (timeZone >= 1) { // Eurasia - nodeUri = 'public.loki.foundation:22023'; + nodeUri = 'publicnode1.rpcnode.stream:29095'; } else if (timeZone <= -4) { // America - nodeUri = 'freyr.imaginary.stream:22023'; + nodeUri = 'publicnode1.rpcnode.stream:29095'; } final node = diff --git a/lib/src/utils/network_service.dart b/lib/src/utils/network_service.dart new file mode 100644 index 0000000..edd467c --- /dev/null +++ b/lib/src/utils/network_service.dart @@ -0,0 +1,19 @@ +import 'dart:async'; + +import 'package:connectivity_plus/connectivity_plus.dart'; + +enum NetworkStatus {online,offline} + +class NetworkService{ + StreamController controller = StreamController(); + + NetworkService(){ + Connectivity().onConnectivityChanged.listen((event) { + controller.add(_networkStatus(event)); + }); + } + + NetworkStatus _networkStatus(ConnectivityResult connectivityResult){ + return connectivityResult == ConnectivityResult.mobile || connectivityResult == ConnectivityResult.wifi ? NetworkStatus.online: NetworkStatus.offline; + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index e36a69a..23f059a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -141,6 +141,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" + connectivity_plus: + dependency: "direct main" + description: + name: connectivity_plus + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.9" + connectivity_plus_linux: + dependency: transitive + description: + name: connectivity_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + connectivity_plus_macos: + dependency: transitive + description: + name: connectivity_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.6" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.2" + connectivity_plus_web: + dependency: transitive + description: + name: connectivity_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.5" + connectivity_plus_windows: + dependency: transitive + description: + name: connectivity_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.2" convert: dependency: transitive description: @@ -169,6 +211,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.3" + dbus: + dependency: transitive + description: + name: dbus + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.3" devicelocale: dependency: "direct main" description: @@ -413,6 +462,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" package_config: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ae979ff..c441d32 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: flutter_svg: ^0.22.0 url_launcher: ^6.0.3 native_updater: ^0.1.0 + connectivity_plus: dev_dependencies: flutter_test: From 3abfdf7c0bbcd3715ca730234550d9d9829e7801 Mon Sep 17 00:00:00 2001 From: SteveJosephh21 Date: Tue, 29 Aug 2023 13:32:44 +0530 Subject: [PATCH 4/4] Update version 1.0.1 --- android/app/src/main/AndroidManifest.xml | 12 ++++++------ .../io/beldex/master_node_monitor/MainActivity.kt | 10 ++++++++-- assets/changelog.yml | 4 ++++ lib/main.dart | 2 +- lib/src/screens/settings_page.dart | 2 +- pubspec.yaml | 2 +- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index bc23f7e..86afba1 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -20,23 +20,23 @@ android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize" android:exported="true"> - + to determine the Window background behind the Flutter UI. –> - + + Flutter's first frame. –> + />--> diff --git a/android/app/src/main/kotlin/io/beldex/master_node_monitor/MainActivity.kt b/android/app/src/main/kotlin/io/beldex/master_node_monitor/MainActivity.kt index bb8b6b9..6629bd3 100644 --- a/android/app/src/main/kotlin/io/beldex/master_node_monitor/MainActivity.kt +++ b/android/app/src/main/kotlin/io/beldex/master_node_monitor/MainActivity.kt @@ -1,6 +1,12 @@ package io.beldex.master_node_monitor -import io.flutter.embedding.android.FlutterActivity +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterFragmentActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugins.GeneratedPluginRegistrant -class MainActivity: FlutterActivity() { +class MainActivity: FlutterFragmentActivity() { + override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + GeneratedPluginRegistrant.registerWith(flutterEngine); + } } diff --git a/assets/changelog.yml b/assets/changelog.yml index 6e57655..16cd7cb 100644 --- a/assets/changelog.yml +++ b/assets/changelog.yml @@ -2,3 +2,7 @@ version: 1.0.0 changes: - Initial release +- + version: 1.0.1 + changes: + - Updated support for Android version 13 diff --git a/lib/main.dart b/lib/main.dart index b58ec8c..569b69c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,7 +22,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'generated/l10n.dart'; -Future main() async { +void main() async { WidgetsFlutterBinding.ensureInitialized(); final appDir = await getApplicationDocumentsDirectory(); diff --git a/lib/src/screens/settings_page.dart b/lib/src/screens/settings_page.dart index 7924cb9..e0b8cec 100644 --- a/lib/src/screens/settings_page.dart +++ b/lib/src/screens/settings_page.dart @@ -151,7 +151,7 @@ class SettingsPage extends BasePage { ), Padding( padding: EdgeInsets.only(left: 35, top: 10), - child: Text("Version 1.0.0",style: TextStyle(fontSize: 16.0,color: BeldexPalette.progressCenterText),), + child: Text("Version 1.0.1",style: TextStyle(fontSize: 16.0,color: BeldexPalette.progressCenterText),), ) ], ); diff --git a/pubspec.yaml b/pubspec.yaml index c441d32..d9001d7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +version: 1.0.1+2 environment: sdk: ">=2.7.0 <3.0.0"