From c0ed25922e6384feb28cfa0c5645155a039ff788 Mon Sep 17 00:00:00 2001 From: primozratej Date: Sun, 2 Jul 2023 13:12:27 +0400 Subject: [PATCH 1/2] Remove shouldInterceptAjaxRequest and move the logic to browser with JS --- lib/pages/opener.dart | 6 ++++++ lib/pages/web_view.dart | 41 +++++++++++++++++++---------------------- lib/util/providers.dart | 6 ++++++ 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/lib/pages/opener.dart b/lib/pages/opener.dart index 04f6c80..37ede5c 100644 --- a/lib/pages/opener.dart +++ b/lib/pages/opener.dart @@ -198,4 +198,10 @@ class OpenerState extends ConsumerState { ), ); } + + @override + void dispose() { + controlLer.urlTextController.dispose(); + super.dispose(); + } } diff --git a/lib/pages/web_view.dart b/lib/pages/web_view.dart index e159c89..c728511 100644 --- a/lib/pages/web_view.dart +++ b/lib/pages/web_view.dart @@ -16,7 +16,6 @@ import 'package:humhub/util/push/push_plugin.dart'; import 'package:humhub/util/providers.dart'; import 'package:loggy/loggy.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:url_launcher/url_launcher.dart'; import 'package:humhub/util/router.dart' as m; import '../components/in_app_browser.dart'; @@ -38,7 +37,6 @@ class WebViewAppState extends ConsumerState { final _options = InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( useShouldOverrideUrlLoading: true, - useShouldInterceptAjaxRequest: true, useShouldInterceptFetchRequest: true, javaScriptEnabled: true, ), @@ -80,9 +78,11 @@ class WebViewAppState extends ConsumerState { pullToRefreshController: _pullToRefreshController, shouldOverrideUrlLoading: _shouldOverrideUrlLoading, onWebViewCreated: _onWebViewCreated, - shouldInterceptAjaxRequest: _shouldInterceptAjaxRequest, shouldInterceptFetchRequest: _shouldInterceptFetchRequest, onLoadStop: _onLoadStop, + onLoadStart: (controller, uri) { + _setAjaxHeadersJQuery(controller); + }, onProgressChanged: _onProgressChanged, onConsoleMessage: (controller, msg) { // Handle the web resource error here @@ -106,6 +106,7 @@ class WebViewAppState extends ConsumerState { Future _shouldOverrideUrlLoading( InAppWebViewController controller, NavigationAction action) async { // 1st check if url is not def. app url and open it in a browser or inApp. + _setAjaxHeadersJQuery(controller); final url = action.request.url!.origin; if (!url.startsWith(manifest.baseUrl)) { authBrowser.launchUrl(action.request); @@ -166,27 +167,12 @@ class WebViewAppState extends ConsumerState { webViewController = controller; } - Future _shouldInterceptAjaxRequest(InAppWebViewController controller, AjaxRequest request) async { - _initialRequest.headers!.forEach((key, value) { - request.headers!.setRequestHeader(key, value); - }); - return request; - } - Future _shouldInterceptFetchRequest(InAppWebViewController controller, FetchRequest request) async { request.headers!.addAll(_initialRequest.headers!); return request; } URLRequest get _initRequest { - //Append random hash to customHeaders in this state the header should always exist. - bool isHideDialog = ref.read(humHubProvider).isHideDialog; - Map customHeaders = {}; - customHeaders.addAll({ - 'x-humhub-app-token': ref.read(humHubProvider).randomHash!, - 'x-humhub-app': ref.read(humHubProvider).appVersion!, - 'x-humhub-app-ostate': isHideDialog ? '1' : '0' - }); final args = ModalRoute.of(context)!.settings.arguments; String? url; if (args is Manifest) { @@ -199,7 +185,7 @@ class WebViewAppState extends ConsumerState { if (args == null) { manifest = m.MyRouter.initParams; } - return URLRequest(url: Uri.parse(url ?? manifest.baseUrl), headers: customHeaders); + return URLRequest(url: Uri.parse(url ?? manifest.baseUrl), headers: ref.read(humHubProvider).customHeaders); } _onLoadStop(InAppWebViewController controller, Uri? url) { @@ -210,6 +196,7 @@ class WebViewAppState extends ConsumerState { source: "document.querySelector('#account-login-form > div.form-group.field-login-rememberme').style.display='none';"); } + _setAjaxHeadersJQuery(controller); _pullToRefreshController?.endRefreshing(); } @@ -228,10 +215,14 @@ class WebViewAppState extends ConsumerState { : PullToRefreshController( options: _pullToRefreshOptions, onRefresh: () async { - if (defaultTargetPlatform == TargetPlatform.android) { + Uri? url = await webViewController.getUrl(); + if (url != null) { + webViewController.loadUrl( + urlRequest: URLRequest( + url: await webViewController.getUrl(), headers: ref.read(humHubProvider).customHeaders), + ); + } else { webViewController.reload(); - } else if (defaultTargetPlatform == TargetPlatform.iOS) { - webViewController.loadUrl(urlRequest: URLRequest(url: await webViewController.getUrl())); } }, ); @@ -261,4 +252,10 @@ class WebViewAppState extends ConsumerState { ), ); } + + Future _setAjaxHeadersJQuery(InAppWebViewController controller) async { + String jsCode = "\$.ajaxSetup({headers: ${jsonEncode(ref.read(humHubProvider).customHeaders).toString()}});"; + dynamic jsResponse = await controller.evaluateJavascript(source: jsCode); + log(jsResponse != null ? jsResponse.toString() : "Script returned null value"); + } } diff --git a/lib/util/providers.dart b/lib/util/providers.dart index 404c521..b2192e4 100644 --- a/lib/util/providers.dart +++ b/lib/util/providers.dart @@ -23,6 +23,12 @@ class HumHubNotifier extends ChangeNotifier { String? get appVersion => _humHubInstance.appVersion; String? get pushToken => _humHubInstance.pushToken; + Map get customHeaders =>{ + 'x-humhub-app-token': randomHash!, + 'x-humhub-app': appVersion!, + 'x-humhub-app-ostate': isHideDialog ? '1' : '0' + }; + void setIsHideOpener(bool isHide) { _humHubInstance.isHideOpener = isHide; _updateSafeStorage(); From 8dc7b4cd34613568c08b33a5f455824c5645db37 Mon Sep 17 00:00:00 2001 From: primozratej Date: Sun, 2 Jul 2023 16:16:24 +0400 Subject: [PATCH 2/2] Implement ConnectivityPlugin. --- ios/Podfile.lock | 10 +++ lib/pages/opener.dart | 1 + lib/pages/web_view.dart | 7 +- lib/util/connectivity_plugin.dart | 71 +++++++++++++++++++ lib/util/opener_controller.dart | 17 ++++- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 26 ++++++- pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 10 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 lib/util/connectivity_plugin.dart diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 0cd8357..123f32c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,6 +1,9 @@ PODS: - "app_settings (3.0.0+1)": - Flutter + - connectivity_plus (0.0.1): + - Flutter + - ReachabilitySwift - Firebase/CoreOnly (10.3.0): - FirebaseCore (= 10.3.0) - Firebase/Messaging (10.3.0): @@ -81,6 +84,7 @@ PODS: - permission_handler_apple (9.0.4): - Flutter - PromisesObjC (2.2.0) + - ReachabilitySwift (5.0.0) - receive_sharing_intent (0.0.1): - Flutter - uni_links (0.0.1): @@ -92,6 +96,7 @@ PODS: DEPENDENCIES: - app_settings (from `.symlinks/plugins/app_settings/ios`) + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - Flutter (from `Flutter`) @@ -119,10 +124,13 @@ SPEC REPOS: - nanopb - OrderedSet - PromisesObjC + - ReachabilitySwift EXTERNAL SOURCES: app_settings: :path: ".symlinks/plugins/app_settings/ios" + connectivity_plus: + :path: ".symlinks/plugins/connectivity_plus/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" firebase_messaging: @@ -154,6 +162,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: app_settings: d103828c9f5d515c4df9ee754dabd443f7cedcf3 + connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a Firebase: f92fc551ead69c94168d36c2b26188263860acd9 firebase_core: 6242f038aefa4582e028536e71312f7e3a41e6bb firebase_messaging: ee61b8065c395a117864224e3031729e656232af @@ -174,6 +183,7 @@ SPEC CHECKSUMS: package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef + ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1 uni_links: d97da20c7701486ba192624d99bffaaffcfc298a url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de diff --git a/lib/pages/opener.dart b/lib/pages/opener.dart index 37ede5c..b2e8beb 100644 --- a/lib/pages/opener.dart +++ b/lib/pages/opener.dart @@ -1,3 +1,4 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:humhub/pages/web_view.dart'; diff --git a/lib/pages/web_view.dart b/lib/pages/web_view.dart index c728511..d83931c 100644 --- a/lib/pages/web_view.dart +++ b/lib/pages/web_view.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:io'; import 'package:app_settings/app_settings.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_app_badger/flutter_app_badger.dart'; @@ -12,6 +13,7 @@ import 'package:humhub/models/manifest.dart'; import 'package:humhub/pages/opener.dart'; import 'package:humhub/util/extensions.dart'; import 'package:humhub/util/notifications/plugin.dart'; +import 'package:humhub/util/opener_controller.dart'; import 'package:humhub/util/push/push_plugin.dart'; import 'package:humhub/util/providers.dart'; import 'package:loggy/loggy.dart'; @@ -20,6 +22,7 @@ import 'package:humhub/util/router.dart' as m; import '../components/in_app_browser.dart'; import '../models/hum_hub.dart'; +import '../util/connectivity_plugin.dart'; class WebViewApp extends ConsumerStatefulWidget { const WebViewApp({super.key}); @@ -80,7 +83,8 @@ class WebViewAppState extends ConsumerState { onWebViewCreated: _onWebViewCreated, shouldInterceptFetchRequest: _shouldInterceptFetchRequest, onLoadStop: _onLoadStop, - onLoadStart: (controller, uri) { + onLoadStart: (controller, uri) async { + bool isConnected = await ConnectivityPlugin.hasConnectivity; _setAjaxHeadersJQuery(controller); }, onProgressChanged: _onProgressChanged, @@ -93,6 +97,7 @@ class WebViewAppState extends ConsumerState { log('Http Error: $description'); }, onLoadError: (InAppWebViewController controller, Uri? url, int code, String message) { + if(code== -1009) NoConnectionDialog.show(context); log('Load Error: $message'); }, ), diff --git a/lib/util/connectivity_plugin.dart b/lib/util/connectivity_plugin.dart new file mode 100644 index 0000000..1117348 --- /dev/null +++ b/lib/util/connectivity_plugin.dart @@ -0,0 +1,71 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class ConnectivityPlugin extends ConsumerStatefulWidget { + final Widget child; + + const ConnectivityPlugin({ + Key? key, + required this.child, + }) : super(key: key); + + @override + ConnectivityPluginState createState() => ConnectivityPluginState(); + + static Future get hasConnectivity async { + ConnectivityResult connectivityResult = await Connectivity().checkConnectivity(); + return connectivityResult != ConnectivityResult.none; + } +} + +class ConnectivityPluginState extends ConsumerState { + @override + Widget build(BuildContext context) { + return StreamBuilder( + stream: Connectivity().onConnectivityChanged, + builder: (context, snap) { + if (snap.hasData) { + // Process the data from the stream + ConnectivityResult result = snap.data!; + if (result == ConnectivityResult.none) { + WidgetsBinding.instance.addPostFrameCallback((_) { + // Use a small delay to show the dialog after the build phase + NoConnectionDialog.show(context); + }); + } + } + return widget.child; + }, + ); + } +} + +class NoConnectionDialog extends StatelessWidget { + const NoConnectionDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('No Connection'), + content: const Text('Please check your internet connection and try again.'), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () { + Navigator.of(context).pop(); // Close the dialog + }, + ), + ], + ); + } + + static show(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return const NoConnectionDialog(); + }, + ); + } +} diff --git a/lib/util/opener_controller.dart b/lib/util/opener_controller.dart index cebd2a6..c721379 100644 --- a/lib/util/opener_controller.dart +++ b/lib/util/opener_controller.dart @@ -1,3 +1,4 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart'; @@ -6,6 +7,7 @@ import 'package:humhub/models/manifest.dart'; import 'package:humhub/util/providers.dart'; import 'package:http/http.dart' as http; import 'api_provider.dart'; +import 'connectivity_plugin.dart'; import 'form_helper.dart'; import 'dart:developer'; @@ -17,6 +19,7 @@ class OpenerController { late String? postcodeErrorMessage; final String formUrlKey = "redirect_url"; final String error404 = "404"; + final String noConnection = "no_connection"; final WidgetRef ref; OpenerController({required this.ref, required this.helper}); @@ -47,6 +50,16 @@ class OpenerController { // Validate the URL format and if !value.isEmpty if (!helper.validate()) return; helper.save(); + + var hasConnection = await ConnectivityPlugin.hasConnectivity; + if (!hasConnection) { + String value = urlTextController.text; + urlTextController.text = noConnection; + helper.validate(); + urlTextController.text = value; + asyncData = null; + return; + } // Get the manifest.json for given url. await findManifest(helper.model[formUrlKey]!); // If manifest.json does not exist the url is incorrect. @@ -72,7 +85,7 @@ class OpenerController { } } - bool get allOk => !(asyncData!.hasError || !doesViewExist); + bool get allOk => !(asyncData == null || asyncData!.hasError || !doesViewExist); Uri assumeUrl(String url) { if (url.startsWith("https://") || url.startsWith("http://")) return Uri.parse(url); @@ -81,7 +94,7 @@ class OpenerController { String? validateUrl(String? value) { if (value == error404) return 'Your HumHub installation does not exist'; - + if (value == noConnection) return 'Please check your internet connection.'; if (value == null || value.isEmpty) { return 'Specify you HumHub location'; } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a133a00..4807cab 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import connectivity_plus import firebase_core import firebase_messaging import flutter_app_badger @@ -15,6 +16,7 @@ import package_info_plus import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) FlutterAppBadgerPlugin.register(with: registry.registrar(forPlugin: "FlutterAppBadgerPlugin")) diff --git a/pubspec.lock b/pubspec.lock index a263c82..308f8b8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -121,6 +121,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + connectivity_plus: + dependency: "direct main" + description: + name: connectivity_plus + sha256: "8599ae9edca5ff96163fca3e36f8e481ea917d1e71cdad912c084b5579913f34" + url: "https://pub.dev" + source: hosted + version: "4.0.1" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + url: "https://pub.dev" + source: hosted + version: "1.2.4" convert: dependency: transitive description: @@ -472,6 +488,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.4.0" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" package_config: dependency: transitive description: @@ -863,4 +887,4 @@ packages: version: "3.1.2" sdks: dart: ">=2.19.0 <3.0.0" - flutter: ">=3.0.1" + flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index abcb08c..d3796a8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,7 @@ dependencies: rive: 0.9.0 auto_size_text: ^3.0.0 mockito: ^5.4.0 + connectivity_plus: ^4.0.1 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 822c7d5..03c149a 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,11 +6,14 @@ #include "generated_plugin_registrant.h" +#include #include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 42df715..f552add 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus flutter_secure_storage_windows permission_handler_windows url_launcher_windows