diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 0630271..2a113c4 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -58,6 +58,8 @@ NSCameraUsageDescription We need access to your camera to take photos and videos for sharing in the app. + NSPhotoLibraryUsageDescription + We need access your photo library to allow you to upload, view, and manage photos within the app. NSAppTransportSecurity NSAllowsArbitraryLoads diff --git a/lib/flavored/web_view.f.dart b/lib/flavored/web_view.f.dart index e8e7de1..8e137a6 100644 --- a/lib/flavored/web_view.f.dart +++ b/lib/flavored/web_view.f.dart @@ -10,16 +10,15 @@ import 'package:humhub/app_flavored.dart'; import 'package:humhub/flavored/models/humhub.f.dart'; import 'package:humhub/util/auth_in_app_browser.dart'; import 'package:humhub/models/channel_message.dart'; +import 'package:humhub/util/black_list_rules.dart'; import 'package:humhub/util/const.dart'; import 'package:humhub/util/extensions.dart'; import 'package:humhub/util/loading_provider.dart'; import 'package:humhub/util/notifications/init_from_push.dart'; -import 'package:humhub/util/notifications/plugin.dart'; import 'package:humhub/util/push/provider.dart'; import 'package:humhub/util/show_dialog.dart'; import 'package:humhub/util/web_view_global_controller.dart'; import 'package:loggy/loggy.dart'; -import 'package:permission_handler/permission_handler.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:humhub/util/file_handler.dart'; @@ -37,8 +36,9 @@ class FlavoredWebViewState extends ConsumerState { late AuthInAppBrowser _authBrowser; HeadlessInAppWebView? headlessWebView; late HumHubF instance; - + final _scaffoldKey = GlobalKey(); late PullToRefreshController pullToRefreshController; + late double downloadProgress = 0; @override void initState() { @@ -73,6 +73,7 @@ class FlavoredWebViewState extends ConsumerState { return WillPopScope( onWillPop: () => exitApp(context, ref), child: Scaffold( + key: _scaffoldKey, backgroundColor: HexColor(instance.manifest.themeColor), body: SafeArea( bottom: false, @@ -121,11 +122,22 @@ class FlavoredWebViewState extends ConsumerState { InAppWebViewController controller, NavigationAction action) async { // 1st check if url is not def. app url and open it in a browser or inApp. WebViewGlobalController.ajaxSetHeaders(headers: instance.customHeaders); - final url = action.request.url!.origin; + final url = action.request.url!.rawValue; + + /// First BLOCK everything that rules out as blocked. + if (BlackListRules.check(url)) { + return NavigationActionPolicy.CANCEL; + } + // For SSO if (!url.startsWith(instance.manifest.baseUrl) && action.isForMainFrame) { _authBrowser.launchUrl(action.request); return NavigationActionPolicy.CANCEL; } + // For all other external links + if (!url.startsWith(instance.manifest.baseUrl) && !action.isForMainFrame) { + await launchUrl(action.request.url!.uriValue, mode: LaunchMode.externalApplication); + return NavigationActionPolicy.CANCEL; + } // 2nd Append customHeader if url is in app redirect and CANCEL the requests without custom headers if (Platform.isAndroid || action.navigationType == NavigationType.LINK_ACTIVATED || @@ -162,7 +174,7 @@ class FlavoredWebViewState extends ConsumerState { Future _onCreateWindow(InAppWebViewController controller, CreateWindowAction createWindowAction) async { final urlToOpen = createWindowAction.request.url; if (urlToOpen == null) return Future.value(false); - if (WebViewGlobalController.openCreateWindowInWebView(ref, urlToOpen.rawValue)) { + if (WebViewGlobalController.openCreateWindowInWebView(url: urlToOpen.rawValue, manifest: instance.manifest)) { controller.loadUrl(urlRequest: createWindowAction.request); return Future.value(false); } @@ -219,12 +231,6 @@ class FlavoredWebViewState extends ConsumerState { headers: instance.customHeaders, ); } - var status = await Permission.notification.status; - // status.isDenied: The user has previously denied the notification permission - // !status.isGranted: The user has never been asked for the notification permission - bool wasAskedBefore = await NotificationPlugin.hasAskedPermissionBefore(); - // ignore: use_build_context_synchronously - if (status != PermissionStatus.granted && !wasAskedBefore) ShowDialog.of(context).notificationPermission(); break; case ChannelAction.updateNotificationCount: if (message.count != null) FlutterAppBadger.updateBadgeCount(message.count!); @@ -275,30 +281,111 @@ class FlavoredWebViewState extends ConsumerState { } void _onDownloadStartRequest(InAppWebViewController controller, DownloadStartRequest downloadStartRequest) async { + PersistentBottomSheetController? persistentController; + //bool isBottomSheetVisible = false; + + // Initialize the download progress + downloadProgress = 0; + + // Timer to control when to show the bottom sheet + Timer? downloadTimer; + bool isDone = false; + FileHandler( - downloadStartRequest: downloadStartRequest, - controller: controller, - onSuccess: (File file, String filename) { - scaffoldMessengerStateKey.currentState?.showSnackBar( - SnackBar( - content: Text('${AppLocalizations.of(context)!.file_download}: $filename'), - action: SnackBarAction( - label: AppLocalizations.of(context)!.open, - onPressed: () { - // Open the downloaded file - OpenFile.open(file.path); - }, - ), - ), - ); - }, - onError: (er) { - scaffoldMessengerStateKey.currentState?.showSnackBar( - SnackBar( - content: Text(AppLocalizations.of(context)!.generic_error), + downloadStartRequest: downloadStartRequest, + controller: controller, + onSuccess: (File file, String filename) async { + // Hide the bottom sheet if it is visible + Navigator.popUntil(context, ModalRoute.withName(WebViewF.path)); + isDone = true; + scaffoldMessengerStateKey.currentState?.showSnackBar( + SnackBar( + content: Text('${AppLocalizations.of(context)!.file_download}: $filename'), + action: SnackBarAction( + label: AppLocalizations.of(context)!.open, + onPressed: () { + OpenFile.open(file.path); + }, ), - ); - }).download(); + ), + ); + }, + onStart: () async { + downloadProgress = 0; + // Start the timer for 1 second + downloadTimer = Timer(const Duration(seconds: 1), () { + // Show the persistent bottom sheet if not already shown + if (!isDone) { + persistentController = _scaffoldKey.currentState!.showBottomSheet((context) { + return Container( + width: MediaQuery.of(context).size.width, + height: 100, + color: const Color(0xff313033), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "${AppLocalizations.of(context)!.downloading}...", + style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white), + ), + Stack( + alignment: Alignment.center, + children: [ + CircularProgressIndicator( + value: downloadProgress / 100, + backgroundColor: Colors.grey, + color: Colors.green, + ), + downloadProgress.toStringAsFixed(0) == "100" + ? const Icon( + Icons.check, + color: Colors.green, + size: 25, + ) + : Text( + downloadProgress.toStringAsFixed(0), + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ], + ), + ], + ), + ), + ); + }); + } + }); + }, + onProgress: (progress) async { + downloadProgress = progress; + if (persistentController != null) { + persistentController!.setState!(() {}); + } + }, + onError: (er) { + downloadTimer?.cancel(); + if (persistentController != null) { + Navigator.popUntil(context, ModalRoute.withName(WebViewF.path)); + } + scaffoldMessengerStateKey.currentState?.showSnackBar( + SnackBar( + content: Text(AppLocalizations.of(context)!.generic_error), + ), + ); + }, + ).download(); + + // Ensure to cancel the timer if download finishes before 1 second + Future.delayed(const Duration(seconds: 1), () { + if (downloadProgress >= 100) { + downloadTimer?.cancel(); + } + }); } @override diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 670f860..8495482 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -23,9 +23,6 @@ "more_info_pro_edition": "Professional Edition", "more_info_pro_edition_url": "https://www.humhub.com/de/professional-edition", - "notification_permission_popup_title": "Benachrichtigungen empfangen", - "notification_permission_popup_content": "Bitte aktiviere Benachrichtigungen für die HumHub App in deinen Geräteeinstellungen.", - "connectivity_popup_title": "Keine Verbindung möglich.", "connectivity_popup_content": "Bitte überprüfe deine Internetverbindung und versuche es erneut.", @@ -37,6 +34,7 @@ "enable_permissions": "Berechtigungen aktivieren.", + "downloading": "Herunterladen", "settings": "Einstellungen", "connect": "Verbinden", "open": "Öffnen", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 45d2fcb..59501ce 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -24,9 +24,6 @@ "more_info_pro_edition": "Professional Edition", "more_info_pro_edition_url": "https://www.humhub.com/en/professional-edition", - "notification_permission_popup_title": "Notification Permission", - "notification_permission_popup_content": "Please enable notifications for HumHub in the device settings", - "connectivity_popup_title": "No Connection", "connectivity_popup_content": "Please check your internet connection and try again.", @@ -38,6 +35,7 @@ "enable_permissions": "Enable permissions.", + "downloading": "Downloading", "settings": "Settings", "connect": "Connect", "open": "Open", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 7b13c99..f1ca775 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -23,9 +23,6 @@ "more_info_pro_edition": "Édition Professionnelle", "more_info_pro_edition_url": "https://www.humhub.com/fr/edition-professionnelle", - "notification_permission_popup_title": "Permission de notification", - "notification_permission_popup_content": "Veuillez activer les notifications pour HumHub dans les paramètres de l'appareil.", - "connectivity_popup_title": "Pas de connexion", "connectivity_popup_content": "Veuillez vérifier votre connexion Internet et réessayer.", @@ -37,6 +34,7 @@ "enable_permissions": "Activer les autorisations.", + "downloading": "Téléchargement", "settings": "Paramètres", "connect": "Connecter", "open": "Ouvrir", diff --git a/lib/pages/web_view.dart b/lib/pages/web_view.dart index 1a0d81d..4451938 100644 --- a/lib/pages/web_view.dart +++ b/lib/pages/web_view.dart @@ -18,16 +18,13 @@ import 'package:humhub/util/extensions.dart'; import 'package:humhub/util/file_handler.dart'; import 'package:humhub/util/loading_provider.dart'; import 'package:humhub/util/notifications/init_from_push.dart'; -import 'package:humhub/util/notifications/plugin.dart'; import 'package:humhub/util/providers.dart'; import 'package:humhub/util/openers/universal_opener_controller.dart'; import 'package:humhub/util/push/provider.dart'; import 'package:humhub/util/router.dart'; -import 'package:humhub/util/show_dialog.dart'; import 'package:humhub/util/web_view_global_controller.dart'; import 'package:loggy/loggy.dart'; import 'package:open_file_plus/open_file_plus.dart'; -import 'package:permission_handler/permission_handler.dart'; import 'package:humhub/util/router.dart' as m; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -41,12 +38,14 @@ class WebView extends ConsumerStatefulWidget { } class WebViewAppState extends ConsumerState { + final _scaffoldKey = GlobalKey(); late AuthInAppBrowser _authBrowser; late Manifest _manifest; late URLRequest _initialRequest; late PullToRefreshController _pullToRefreshController; HeadlessInAppWebView? _headlessWebView; bool _isInit = false; + late double downloadProgress = 0; final _settings = InAppWebViewSettings( useShouldOverrideUrlLoading: true, @@ -91,6 +90,7 @@ class WebViewAppState extends ConsumerState { @override Widget build(BuildContext context) { return Scaffold( + key: _scaffoldKey, backgroundColor: HexColor(_manifest.themeColor), body: SafeArea( bottom: false, @@ -144,17 +144,18 @@ class WebViewAppState extends ConsumerState { InAppWebViewController controller, NavigationAction action) async { WebViewGlobalController.ajaxSetHeaders(headers: ref.read(humHubProvider).customHeaders); - //Open in external browser final url = action.request.url!.rawValue; /// First BLOCK everything that rules out as blocked. if (BlackListRules.check(url)) { return NavigationActionPolicy.CANCEL; } + // For SSO if (!url.startsWith(_manifest.baseUrl) && action.isForMainFrame) { _authBrowser.launchUrl(action.request); return NavigationActionPolicy.CANCEL; } - if (!action.isForMainFrame) { + // For all other external links + if (!url.startsWith(_manifest.baseUrl) && !action.isForMainFrame) { await launchUrl(action.request.url!.uriValue, mode: LaunchMode.externalApplication); return NavigationActionPolicy.CANCEL; } @@ -198,7 +199,10 @@ class WebViewAppState extends ConsumerState { WebUri? urlToOpen = createWindowAction.request.url; if (urlToOpen == null) return Future.value(false); - if (WebViewGlobalController.openCreateWindowInWebView(ref, urlToOpen.rawValue)) { + if (WebViewGlobalController.openCreateWindowInWebView( + url: urlToOpen.rawValue, + manifest: ref.read(humHubProvider).manifest!, + )) { controller.loadUrl(urlRequest: createWindowAction.request); return Future.value(false); } @@ -265,12 +269,6 @@ class WebViewAppState extends ConsumerState { headers: ref.read(humHubProvider).customHeaders, ); } - var status = await Permission.notification.status; - // status.isDenied: The user has previously denied the notification permission - // !status.isGranted: The user has never been asked for the notification permission - bool wasAskedBefore = await NotificationPlugin.hasAskedPermissionBefore(); - // ignore: use_build_context_synchronously - if (status != PermissionStatus.granted && !wasAskedBefore) ShowDialog.of(context).notificationPermission(); break; case ChannelAction.updateNotificationCount: if (message.count != null) FlutterAppBadger.updateBadgeCount(message.count!); @@ -324,30 +322,112 @@ class WebViewAppState extends ConsumerState { } void _onDownloadStartRequest(InAppWebViewController controller, DownloadStartRequest downloadStartRequest) async { + PersistentBottomSheetController? persistentController; + //bool isBottomSheetVisible = false; + + // Initialize the download progress + downloadProgress = 0; + + // Timer to control when to show the bottom sheet + Timer? downloadTimer; + bool isDone = false; + FileHandler( - downloadStartRequest: downloadStartRequest, - controller: controller, - onSuccess: (File file, String filename) { - scaffoldMessengerStateKey.currentState?.showSnackBar( - SnackBar( - content: Text('${AppLocalizations.of(context)!.file_download}: $filename'), - action: SnackBarAction( - label: AppLocalizations.of(context)!.open, - onPressed: () { - // Open the downloaded file - OpenFile.open(file.path); - }, - ), - ), - ); - }, - onError: (er) { - scaffoldMessengerStateKey.currentState?.showSnackBar( - SnackBar( - content: Text(AppLocalizations.of(context)!.generic_error), + downloadStartRequest: downloadStartRequest, + controller: controller, + onSuccess: (File file, String filename) async { + // Hide the bottom sheet if it is visible + Navigator.popUntil(context, ModalRoute.withName(WebView.path)); + isDone = true; + scaffoldMessengerStateKey.currentState?.showSnackBar( + SnackBar( + content: Text('${AppLocalizations.of(context)!.file_download}: $filename'), + action: SnackBarAction( + label: AppLocalizations.of(context)!.open, + onPressed: () { + //file.open(); + OpenFile.open(file.path); + }, ), - ); - }).download(); + ), + ); + }, + onStart: () async { + downloadProgress = 0; + // Start the timer for 1 second + downloadTimer = Timer(const Duration(seconds: 1), () { + // Show the persistent bottom sheet if not already shown + if (!isDone) { + persistentController = _scaffoldKey.currentState!.showBottomSheet((context) { + return Container( + width: MediaQuery.of(context).size.width, + height: 100, + color: const Color(0xff313033), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "${AppLocalizations.of(context)!.downloading}...", + style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white), + ), + Stack( + alignment: Alignment.center, + children: [ + CircularProgressIndicator( + value: downloadProgress / 100, + backgroundColor: Colors.grey, + color: Colors.green, + ), + downloadProgress.toStringAsFixed(0) == "100" + ? const Icon( + Icons.check, + color: Colors.green, + size: 25, + ) + : Text( + downloadProgress.toStringAsFixed(0), + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ], + ), + ], + ), + ), + ); + }); + } + }); + }, + onProgress: (progress) async { + downloadProgress = progress; + if (persistentController != null) { + persistentController!.setState!(() {}); + } + }, + onError: (er) { + downloadTimer?.cancel(); + if (persistentController != null) { + Navigator.popUntil(context, ModalRoute.withName(WebView.path)); + } + scaffoldMessengerStateKey.currentState?.showSnackBar( + SnackBar( + content: Text(AppLocalizations.of(context)!.generic_error), + ), + ); + }, + ).download(); + + // Ensure to cancel the timer if download finishes before 1 second + Future.delayed(const Duration(seconds: 1), () { + if (downloadProgress >= 100) { + downloadTimer?.cancel(); + } + }); } @override diff --git a/lib/util/file_handler.dart b/lib/util/file_handler.dart index 0f9e3ed..bc0f9aa 100644 --- a/lib/util/file_handler.dart +++ b/lib/util/file_handler.dart @@ -12,35 +12,68 @@ class FileHandler { final String? filename; final Function(Exception ex)? onError; final Function(File file, String filename) onSuccess; + final Function(double progress)? onProgress; + final Function()? onStart; static const String _jsCode = """ - function downloadFile(url) { - fetch(url, { - headers: { - // Include necessary headers for authentication if needed - 'Authorization': 'Bearer ' - } - }) - .then(response => response.blob()) - .then(blob => { - const reader = new FileReader(); - reader.readAsDataURL(blob); - reader.onloadend = function() { - const base64data = reader.result; - // Send the base64 content to Flutter - window.flutter_inappwebview.callHandler('downloadFile', base64data); - } - }) - .catch(error => console.error('Error downloading file:', error)); - } + function downloadFile(url) { + fetch(url, { + headers: { + // Include necessary headers for authentication if needed + 'Authorization': 'Bearer ' + } + }) + .then(response => { + const reader = response.body.getReader(); + const contentLength = response.headers.get('Content-Length'); + let receivedLength = 0; + let chunks = []; // array of received binary chunks (comprises the body) + + return new ReadableStream({ + start(controller) { + function push() { + reader.read().then(({ done, value }) => { + if (done) { + const blob = new Blob(chunks); // Create blob from chunks + const fileReader = new FileReader(); + fileReader.readAsDataURL(blob); + fileReader.onloadend = function() { + const base64data = fileReader.result; + // Send the base64 content to Flutter + window.flutter_inappwebview.callHandler('downloadFile', base64data); + } + return; + } + + chunks.push(value); // Store received chunk + receivedLength += value.length; + + if (contentLength) { + // Calculate progress percentage + const progress = (receivedLength / contentLength) * 100; + // Send the progress to Flutter + window.flutter_inappwebview.callHandler('onProgress', progress); + } + + push(); // Call the push function again to read the next chunk + }); + } + + push(); // Start reading the data + } + }); + }) + .catch(error => console.error('Error downloading file:', error)); + } """; - const FileHandler({ - required this.controller, - required this.downloadStartRequest, - required this.onSuccess, - this.filename, - this.onError, - }); + const FileHandler( + {required this.controller, + required this.downloadStartRequest, + required this.onSuccess, + this.filename, + this.onError, + this.onProgress, + this.onStart}); download() { PermissionHandler.runWithPermissionCheck( @@ -51,6 +84,9 @@ class FileHandler { _download() async { try { + if (onStart != null) { + onStart!(); + } await controller.evaluateJavascript(source: _jsCode); await controller.evaluateJavascript(source: "downloadFile('${downloadStartRequest.url.toString()}');"); controller.addJavaScriptHandler( @@ -60,11 +96,19 @@ class FileHandler { var (file, filename) = await _saveFile(base64Data); onSuccess(file, filename); }); + + controller.addJavaScriptHandler( + handlerName: 'onProgress', + callback: (args) { + double progress = double.parse(args[0].toString()); + if (onProgress != null) { + onProgress!(progress); + } + }); } catch (er) { if (er is Exception && onError != null) { onError!(er); } - //rethrow; } } diff --git a/lib/util/notifications/plugin.dart b/lib/util/notifications/plugin.dart index 257a1f9..f79ed02 100644 --- a/lib/util/notifications/plugin.dart +++ b/lib/util/notifications/plugin.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:humhub/util/notifications/service.dart'; -import 'package:shared_preferences/shared_preferences.dart'; class NotificationPlugin extends StatefulWidget { final Widget child; @@ -18,14 +17,6 @@ class NotificationPlugin extends StatefulWidget { return plugin!; } - static Future hasAskedPermissionBefore() async { - String key = 'was_asked_before'; - SharedPreferences prefs = await SharedPreferences.getInstance(); - var data = prefs.getBool(key) ?? false; - prefs.setBool(key, true); - return data; - } - @override NotificationPluginState createState() => NotificationPluginState(); } diff --git a/lib/util/permission_handler.dart b/lib/util/permission_handler.dart index 0217e21..786957c 100644 --- a/lib/util/permission_handler.dart +++ b/lib/util/permission_handler.dart @@ -6,10 +6,37 @@ import 'package:permission_handler/permission_handler.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class PermissionHandler { - // Static method that takes a list of permissions and handles requests - static Future requestPermissions(List permissions) async => await permissions.request(); + /// A static method that takes a list of permissions and handles permission requests. + /// It only requests permissions that have not yet been granted ([permission.isGranted]) and are not permanently denied ([permission.isPermanentlyDenied]). + /// + /// **Android:** This will be triggered every time if the user closes the permission modal without providing input. + /// **iOS:** Permissions will only be requested once, as the modal is required before continuing. + /// [permissions] - A list of [Permission] objects to be requested. + /// This method ensures that unnecessary permission requests are avoided, improving user experience. + static Future requestPermissions(List permissions) async { + // Filter permissions asynchronously + List toRequest = []; + for (var permission in permissions) { + if (!(await permission.isGranted) && !(await permission.isPermanentlyDenied)) { + toRequest.add(permission); + } + } + // Request the permissions that are not granted + if (toRequest.isNotEmpty) { + await toRequest.request(); + } + } - // Static method to check permissions before executing a function + /// A static method to check if all the necessary permissions are granted before executing a given action. + /// + /// If all required permissions are granted, the provided [action] is executed. + /// If any permissions are missing on Android, a [SnackBar] is shown informing the user to enable the required permissions. + /// **Parameters:** + /// - [permissions]: A list of [Permission] objects that need to be checked. + /// - [action]: A callback function that will be executed if the required permissions are granted. + /// **Android:** If the user closes the permissions dialog without providing input, the method won't proceed with the action. + /// **iOS:** Permissions are required before continuing, so the action will proceed automatically once permissions are granted. + /// If permissions are not granted, the user will be prompted to open the app settings. static Future runWithPermissionCheck({ required List permissions, required Function action, diff --git a/lib/util/show_dialog.dart b/lib/util/show_dialog.dart index 19a20cd..e4c0b18 100644 --- a/lib/util/show_dialog.dart +++ b/lib/util/show_dialog.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:app_settings/app_settings.dart'; class ShowDialog { final BuildContext context; @@ -11,31 +10,6 @@ class ShowDialog { return ShowDialog(context); } - void notificationPermission() { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text(AppLocalizations.of(context)!.notification_permission_popup_title), - content: Text(AppLocalizations.of(context)!.notification_permission_popup_content), - actions: [ - TextButton( - child: Text(AppLocalizations.of(context)!.enable), - onPressed: () { - AppSettings.openAppSettings(); - Navigator.pop(context); - }, - ), - TextButton( - child: Text(AppLocalizations.of(context)!.skip), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ), - ); - } - noInternetPopup() { showDialog( context: context, diff --git a/lib/util/web_view_global_controller.dart b/lib/util/web_view_global_controller.dart index 25d5176..8bd7ba1 100644 --- a/lib/util/web_view_global_controller.dart +++ b/lib/util/web_view_global_controller.dart @@ -1,8 +1,6 @@ import 'dart:convert'; - import 'package:flutter_inappwebview/flutter_inappwebview.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:humhub/util/providers.dart'; +import 'package:humhub/models/manifest.dart'; class WebViewGlobalController { static InAppWebViewController? _value; @@ -21,8 +19,8 @@ class WebViewGlobalController { /// [ref] is reference to the app state. /// [url] is the URL to evaluate. /// @return `true` if the URL should open in a new window, `false` otherwise. - static bool openCreateWindowInWebView(WidgetRef ref, String url) { - String? baseUrl = ref.read(humHubProvider).manifest?.baseUrl; + static bool openCreateWindowInWebView({required String url, required Manifest manifest}) { + String? baseUrl = manifest.baseUrl; if (url.startsWith('$baseUrl/file/file/download')) return true; if (url.startsWith('$baseUrl/u')) return true; if (url.startsWith('$baseUrl/s')) return true;