From e617bb9e72dcf81c15f17c6cee87ddf5691b92a2 Mon Sep 17 00:00:00 2001 From: Frank Merkel <138444693+frankmer@users.noreply.github.com> Date: Tue, 31 Oct 2023 17:25:55 +0100 Subject: [PATCH 1/8] . --- lib/state_notifiers/token_notifier.dart | 10 +- lib/utils/network_utils.dart | 5 +- lib/utils/riverpod_providers.dart | 19 +++ lib/views/main_view/main_view.dart | 15 ++- lib/widgets/connectivity_status_bar.dart | 144 +++++++++++++++++++++++ 5 files changed, 181 insertions(+), 12 deletions(-) create mode 100644 lib/widgets/connectivity_status_bar.dart diff --git a/lib/state_notifiers/token_notifier.dart b/lib/state_notifiers/token_notifier.dart index b9194309f..636b4effd 100644 --- a/lib/state_notifiers/token_notifier.dart +++ b/lib/state_notifiers/token_notifier.dart @@ -385,12 +385,12 @@ class TokenNotifier extends StateNotifier { message: AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutFailed(token.label, response.statusCode) + message, duration: const Duration(seconds: 3), ); - } on FormatException catch (_) { + } on FormatException catch (e) { // Format Exception is thrown if the response body is not a valid json. This happens if the server is not reachable. - showMessage( - message: AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutNoConnectionToServer(token.label), - duration: const Duration(seconds: 3), - ); + if (globalRef != null) { + globalRef!.read(statusMessageProvider.notifier).state = + (AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutFailed(token.label, response.statusCode), null); + } } updateToken(token, (p0) => p0.copyWith(rolloutState: PushTokenRollOutState.sendRSAPublicKeyFailed)); diff --git a/lib/utils/network_utils.dart b/lib/utils/network_utils.dart index b39bace4d..bb89fcbc8 100644 --- a/lib/utils/network_utils.dart +++ b/lib/utils/network_utils.dart @@ -25,6 +25,7 @@ import 'package:http/http.dart'; import 'package:http/io_client.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:privacyidea_authenticator/utils/logger.dart'; +import 'package:privacyidea_authenticator/utils/riverpod_providers.dart'; import 'package:privacyidea_authenticator/utils/view_utils.dart'; class PrivacyIdeaIOClient { @@ -46,7 +47,9 @@ class PrivacyIdeaIOClient { try { await ioClient.post(url, body: ''); } on SocketException { - // ignore + globalRef?.read(statusMessageProvider.notifier).state = ('Connection failed', 'Please check your internet connection.'); + } on ClientException { + globalRef?.read(statusMessageProvider.notifier).state = ('Connection failed', 'Please check your internet connection.'); } finally { ioClient.close(); } diff --git a/lib/utils/riverpod_providers.dart b/lib/utils/riverpod_providers.dart index 5efc537f3..1b453e0a1 100644 --- a/lib/utils/riverpod_providers.dart +++ b/lib/utils/riverpod_providers.dart @@ -1,3 +1,6 @@ +import 'dart:developer'; + +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -124,5 +127,21 @@ final draggingSortableProvider = StateProvider((ref) { return null; }); +final connectivityProvider = StreamProvider((ref) { + Logger.info("New connectivityProvider created"); + return Connectivity().onConnectivityChanged; +}); + +final statusMessageProvider = StateProvider<(String, String?)?>((ref) { + Logger.info("New connectionStateProvider created"); + final next = ref.watch(connectivityProvider).asData?.value; + if (next == null || next == ConnectivityResult.none) { + log("ConnectionState is $next"); + return ('No Connection', null); + } + log("ConnectionState is $next"); + return null; +}); + /// Only used for the app customizer final applicationCustomizerProvider = StateProvider((ref) => ApplicationCustomization.defaultCustomization); diff --git a/lib/views/main_view/main_view.dart b/lib/views/main_view/main_view.dart index 48d982956..e6463dcad 100644 --- a/lib/views/main_view/main_view.dart +++ b/lib/views/main_view/main_view.dart @@ -5,6 +5,7 @@ import 'package:flutterlifecyclehooks/flutterlifecyclehooks.dart'; import '../../model/states/app_state.dart'; import '../../utils/logger.dart'; import '../../utils/riverpod_providers.dart'; +import '../../widgets/connectivity_status_bar.dart'; import 'main_view_widgets/main_view_navigation_bar.dart'; import 'main_view_widgets/main_view_tokens_list.dart'; @@ -53,12 +54,14 @@ class _MainViewState extends ConsumerState with LifecycleMixin { child: widget.appIcon, ), ), - body: Stack( - clipBehavior: Clip.antiAliasWithSaveLayer, - children: [ - MainViewTokensList(nestedScrollViewKey: globalKey), - const MainViewNavigationBar(), - ], + body: ConnectivityStatusBar( + child: Stack( + clipBehavior: Clip.antiAliasWithSaveLayer, + children: [ + MainViewTokensList(nestedScrollViewKey: globalKey), + const MainViewNavigationBar(), + ], + ), ), ); } diff --git a/lib/widgets/connectivity_status_bar.dart b/lib/widgets/connectivity_status_bar.dart new file mode 100644 index 000000000..39b7e0623 --- /dev/null +++ b/lib/widgets/connectivity_status_bar.dart @@ -0,0 +1,144 @@ +import 'dart:collection'; +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacyidea_authenticator/utils/text_size.dart'; + +import '../utils/riverpod_providers.dart'; + +class ConnectivityStatusBar extends ConsumerStatefulWidget { + final Widget child; + const ConnectivityStatusBar({super.key, required this.child}); + + @override + ConsumerState createState() => _ConnectivityStatusBarState(); +} + +class _ConnectivityStatusBarState extends ConsumerState { + (String, String?)? previousStatusMessage; + (String, String?)? currentStatusMessage; + + late Function(DismissDirection) onDismissed; + + OverlayEntry? statusbarOverlay; + ScaffoldFeatureController? snackbarController; + + @override + Widget build(BuildContext context) { + final newStatusMessage = ref.watch(statusMessageProvider); + if (newStatusMessage != previousStatusMessage && newStatusMessage != currentStatusMessage) { + previousStatusMessage = newStatusMessage; + _addToQueueIfNotInQueue(newStatusMessage); + } + return widget.child; + } + + @override + void initState() { + onDismissed = (direction) { + setState(() { + currentStatusMessage = null; + statusbarOverlay!.remove(); + statusbarOverlay = null; + log("Removing statusbar overlay"); + _tryPop(); + }); + }; + + super.initState(); + } + + Queue<(String, String?)> statusbarQueue = Queue(); + + void _addToQueueIfNotInQueue((String, String?)? statusMessage) { + if (statusMessage == null) return; + if (!statusbarQueue.contains(statusMessage) && currentStatusMessage != statusMessage) { + statusbarQueue.add(statusMessage); + } + _tryPop(); + } + + void _tryPop() { + if (currentStatusMessage == null && statusbarQueue.isNotEmpty) { + currentStatusMessage = statusbarQueue.removeFirst(); + _showStatusbarOverlay(currentStatusMessage!); + } + } + + void _showStatusbarOverlay((String, String?) statusMessage) { + final statusText = statusMessage.$1; + final statusSubText = statusMessage.$2; + if (statusbarOverlay != null) { + statusbarOverlay?.remove(); + statusbarOverlay = null; + } + + statusbarOverlay = + OverlayEntry(builder: (context) => StatusBarOverlayEntry(onDismissed: onDismissed, statusText: statusText, statusSubText: statusSubText)); + log("Showing statusbar overlay"); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + Overlay.of(context).insert(statusbarOverlay!); + }); + } +} + +class StatusBarOverlayEntry extends StatefulWidget { + final String statusText; + final String? statusSubText; + final Function(DismissDirection) onDismissed; + + const StatusBarOverlayEntry({super.key, required this.statusText, required this.onDismissed, this.statusSubText}); + + @override + State createState() => _StatusBarOverlayEntryState(); +} + +class _StatusBarOverlayEntryState extends State with SingleTickerProviderStateMixin { + bool isFirstFrame = false; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + if (!isFirstFrame) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + setState(() { + isFirstFrame = true; + }); + }); + } + return AnimatedPositioned( + top: isFirstFrame ? 30 : -textSizeOf(widget.statusText, Theme.of(context).textTheme.bodyLarge!).height - 10, + left: 10, + right: 10, + curve: Curves.easeOut, + duration: const Duration(milliseconds: 250), + child: Material( + color: Colors.transparent, + child: Dismissible( + onDismissed: (direction) { + widget.onDismissed(direction); + }, + key: const Key('statusbarOverlay'), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + color: Theme.of(context).colorScheme.error, + ), + padding: const EdgeInsets.all(10.0), + child: Center( + child: Text( + widget.statusText, + style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Colors.white), + ), + ), + ), + ), + ), + ); + } +} From 8eb5595ccc1000b34d1fb8170f165b1424152623 Mon Sep 17 00:00:00 2001 From: Frank Merkel <138444693+frankmer@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:02:11 +0100 Subject: [PATCH 2/8] works fine --- lib/l10n/app_cs.arb | 32 +++- lib/l10n/app_de.arb | 32 +++- lib/l10n/app_en.arb | 32 +++- lib/l10n/app_es.arb | 32 +++- lib/l10n/app_fr.arb | 32 +++- lib/l10n/app_localizations.dart | 34 +++- lib/l10n/app_localizations_cs.dart | 20 ++- lib/l10n/app_localizations_de.dart | 20 ++- lib/l10n/app_localizations_en.dart | 20 ++- lib/l10n/app_localizations_es.dart | 20 ++- lib/l10n/app_localizations_fr.dart | 20 ++- lib/l10n/app_localizations_nl.dart | 20 ++- lib/l10n/app_localizations_pl.dart | 22 ++- lib/l10n/app_nl.arb | 32 +++- lib/l10n/app_pl.arb | 34 +++- lib/main_netknights.dart | 2 +- lib/state_notifiers/token_notifier.dart | 33 ++-- lib/utils/push_provider.dart | 51 ++++-- lib/utils/riverpod_providers.dart | 11 +- lib/utils/text_size.dart | 6 +- lib/utils/utils.dart | 66 ------- lib/views/main_view/main_view.dart | 17 +- .../main_view_tokens_list.dart | 3 +- .../widgets/push_tokens_view_list.dart | 3 +- lib/widgets/app_wrapper.dart | 24 +++ lib/widgets/connectivity_status_bar.dart | 163 ++++++++++++++---- lib/widgets/two_step_dialog.dart | 4 +- .../push_request_notifier_test.dart | 1 - 28 files changed, 552 insertions(+), 234 deletions(-) diff --git a/lib/l10n/app_cs.arb b/lib/l10n/app_cs.arb index 84b48504a..4e82acd9d 100644 --- a/lib/l10n/app_cs.arb +++ b/lib/l10n/app_cs.arb @@ -180,16 +180,22 @@ "@tokensDoNotSupportSynchronization": { "description": "Informs the user that the following tokens cannot be synchronized as they do not support that." }, - "errorRollOutFailed": "Registrace tokenu {name} selhala. Kód chyby: {errorCode}", + "errorRollOutFailed": "Registrace tokenu {name} selhala.", "@errorRollOutFailed": { "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "type": "text", "placeholders": { "name": { "example": "PUSH1234A" - }, - "errorCode": { - "example": "500" + } + } + }, + "statusCode": "Stavový kód: {statusCode}", + "@statusCode": { + "description": "Tells the user the status code of the error.", + "placeholders": { + "statusCode": { + "example": "400" } } }, @@ -219,9 +225,21 @@ "@unexpectedError": { "description": "Title of page report mode." }, - "pollingFailNoNetworkConnection": "Stahování selhalo. Server není dostupný.", - "@pollingFailNoNetworkConnection": { - "description": "Tells the user that the roll-out failed because no network connection is available." + "pollingFailed": "Dotaz se nezdařil.", + "@pollingFailed": { + "description": "Tells the user that the polling failed." + }, + "noNetworkConnection": "No network connection.", + "@noNetworkConnection": { + "description": "Tells the user that there is no network connection." + }, + "serverNotReachable": "Na server se nepodařilo dovolat.", + "@serverNotReachable": { + "description": "Tells the user that the server could not be reached." + }, + "couldNotSignMessage": "Zprávu se nepodařilo podepsat.", + "@couldNotSignMessage": { + "description": "Tells the user that the message could not be signed." }, "useDeviceLocaleTitle": "Použít jazyk zařízení", "@useDeviceLocaleTitle": { diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index c837d1083..b146273c1 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -178,15 +178,21 @@ "@tokensDoNotSupportSynchronization": { "description": "Informs the user that the following tokens cannot be synchronized as they do not support that." }, - "errorRollOutFailed": "Ausrollen von {name} ist fehlgeschlagen. Fehlercode: {errorCode}", + "errorRollOutFailed": "Ausrollen von {name} ist fehlgeschlagen.", "@errorRollOutFailed": { "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "placeholders": { "name": { "example": "PUSH1234A" - }, - "errorCode": { - "example": "500" + } + } + }, + "statusCode": "Statuscode: {statusCode}", + "@statusCode": { + "description": "Tells the user the status code of the error.", + "placeholders": { + "statusCode": { + "example": "400" } } }, @@ -212,9 +218,21 @@ "@unexpectedError": { "description": "Title of page report mode." }, - "pollingFailNoNetworkConnection": "Abfrage fehlgeschlagen, der Server ist nicht erreichbar.", - "@pollingFailNoNetworkConnection": { - "description": "Tells the user that the roll-out failed because no network connection is available." + "pollingFailed": "Abfrage fehlgeschlagen.", + "@pollingFailed": { + "description": "Tells the user that the polling failed." + }, + "noNetworkConnection": "No network connection.", + "@noNetworkConnection": { + "description": "Tells the user that there is no network connection." + }, + "serverNotReachable": "Der Server konnte nicht erreicht werden.", + "@serverNotReachable": { + "description": "Tells the user that the server could not be reached." + }, + "couldNotSignMessage": "Nachricht konnte nicht signiert werden.", + "@couldNotSignMessage": { + "description": "Tells the user that the message could not be signed." }, "useDeviceLocaleTitle": "Nutze Systemsprache", "@useDeviceLocaleTitle": { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 5f42ef8cb..6ac4fa5d1 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -170,15 +170,21 @@ "@tokensDoNotSupportSynchronization": { "description": "Informs the user that the following tokens cannot be synchronized as they do not support that." }, - "errorRollOutFailed": "Rolling out token {name} failed.\nError code: {errorCode}", + "errorRollOutFailed": "Rolling out token {name} failed.", "@errorRollOutFailed": { "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "placeholders": { "name": { "example": "PUSH1234A" - }, - "errorCode": { - "example": "500" + } + } + }, + "statusCode": "Status code: {statusCode}", + "@statusCode": { + "description": "Tells the user the status code of the error.", + "placeholders": { + "statusCode": { + "example": "400" } } }, @@ -211,9 +217,21 @@ "@unexpectedError": { "description": "Title of page report mode." }, - "pollingFailNoNetworkConnection": "Polling failed. Server cannot be reached.", - "@pollingFailNoNetworkConnection": { - "description": "Tells the user that the roll-out failed because no network connection is available." + "pollingFailed": "Polling failed.", + "@pollingFailed": { + "description": "Tells the user that the polling failed." + }, + "noNetworkConnection": "No network connection.", + "@noNetworkConnection": { + "description": "Tells the user that there is no network connection." + }, + "serverNotReachable": "The server could not be reached.", + "@serverNotReachable": { + "description": "Tells the user that the server could not be reached." + }, + "couldNotSignMessage": "Could not sign message.", + "@couldNotSignMessage": { + "description": "Tells the user that the message could not be signed." }, "useDeviceLocaleTitle": "Use device language", "@useDeviceLocaleTitle": { diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 6c8e95f04..74a67c618 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -178,15 +178,21 @@ "@tokensDoNotSupportSynchronization": { "description": "Informs the user that the following tokens cannot be synchronized as they do not support that." }, - "errorRollOutFailed": "Error en la extracción de el token {name}. Código de error: {errorCode}", + "errorRollOutFailed": "Error en la extracción de el token {name}.", "@errorRollOutFailed": { "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "placeholders": { "name": { "example": "PUSH1234A" - }, - "errorCode": { - "example": "500" + } + } + }, + "statusCode": "Código de estado: {statusCode}", + "@statusCode": { + "description": "Tells the user the status code of the error.", + "placeholders": { + "statusCode": { + "example": "400" } } }, @@ -215,9 +221,21 @@ "@unexpectedError": { "description": "Title of page report mode." }, - "pollingFailNoNetworkConnection": "Error de sondeo. No se puede acceder al servidor.", - "@pollingFailNoNetworkConnection": { - "description": "Tells the user that the roll-out failed because no network connection is available." + "pollingFailed": "Consulta fallida.", + "@pollingFailed": { + "description": "Tells the user that the polling failed." + }, + "noNetworkConnection": "No network connection.", + "@noNetworkConnection": { + "description": "Tells the user that there is no network connection." + }, + "serverNotReachable": "No se ha podido acceder al servidor.", + "@serverNotReachable": { + "description": "Tells the user that the server could not be reached." + }, + "couldNotSignMessage": "No se ha podido firmar el mensaje.", + "@couldNotSignMessage": { + "description": "Tells the user that the message could not be signed." }, "useDeviceLocaleTitle": "Utiliza el idioma del teléfono", "@useDeviceLocaleTitle": { diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 4aafe4020..c3fb4cd05 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -180,16 +180,22 @@ "@tokensDoNotSupportSynchronization": { "description": "Informs the user that the following tokens cannot be synchronized as they do not support that." }, - "errorRollOutFailed": "Le déploiement du jeton {name} a échoué. Erreur réseau: {errorCode}", + "errorRollOutFailed": "Le déploiement du jeton {name} a échoué.", "@errorRollOutFailed": { "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "type": "text", "placeholders": { "name": { "example": "PUSH1234A" - }, - "errorCode": { - "example": "500" + } + } + }, + "statusCode": "Code d'état : {statusCode}", + "@statusCode": { + "description": "Tells the user the status code of the error.", + "placeholders": { + "statusCode": { + "example": "400" } } }, @@ -219,9 +225,21 @@ "@unexpectedError": { "description": "Title of page report mode." }, - "pollingFailNoNetworkConnection": "L'interrogation a échoué. Le serveur est injoignable.", - "@pollingFailNoNetworkConnection": { - "description": "Tells the user that the roll-out failed because no network connection is available." + "pollingFailed": "Échec de la requête.", + "@pollingFailed": { + "description": "Tells the user that the polling failed." + }, + "noNetworkConnection": "No network connection.", + "@noNetworkConnection": { + "description": "Tells the user that there is no network connection." + }, + "serverNotReachable": "Le serveur n'a pas pu être atteint.", + "@serverNotReachable": { + "description": "Tells the user that the server could not be reached." + }, + "couldNotSignMessage": "Impossible de signer le message.", + "@couldNotSignMessage": { + "description": "Tells the user that the message could not be signed." }, "useDeviceLocaleTitle": "Utiliser la langue de l'appareil", "@useDeviceLocaleTitle": { diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 1d04a36fd..0d9d72e2b 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -346,8 +346,14 @@ abstract class AppLocalizations { /// Tells the user that the token could not be rolled out, because a network error occurred. /// /// In en, this message translates to: - /// **'Rolling out token {name} failed.\nError code: {errorCode}'** - String errorRollOutFailed(Object name, Object errorCode); + /// **'Rolling out token {name} failed.'** + String errorRollOutFailed(Object name); + + /// Tells the user the status code of the error. + /// + /// In en, this message translates to: + /// **'Status code: {statusCode}'** + String statusCode(Object statusCode); /// Tells the user that synchronizing the push tokens failed because the server could not be reached. /// @@ -385,11 +391,29 @@ abstract class AppLocalizations { /// **'An unexpected error occurred.'** String get unexpectedError; - /// Tells the user that the roll-out failed because no network connection is available. + /// Tells the user that the polling failed. + /// + /// In en, this message translates to: + /// **'Polling failed.'** + String get pollingFailed; + + /// Tells the user that there is no network connection. + /// + /// In en, this message translates to: + /// **'No network connection.'** + String get noNetworkConnection; + + /// Tells the user that the server could not be reached. + /// + /// In en, this message translates to: + /// **'The server could not be reached.'** + String get serverNotReachable; + + /// Tells the user that the message could not be signed. /// /// In en, this message translates to: - /// **'Polling failed. Server cannot be reached.'** - String get pollingFailNoNetworkConnection; + /// **'Could not sign message.'** + String get couldNotSignMessage; /// Title of the switch tile where using the devices language can be enabled. /// diff --git a/lib/l10n/app_localizations_cs.dart b/lib/l10n/app_localizations_cs.dart index e2357d8df..5aeedc0c8 100644 --- a/lib/l10n/app_localizations_cs.dart +++ b/lib/l10n/app_localizations_cs.dart @@ -129,8 +129,13 @@ class AppLocalizationsCs extends AppLocalizations { String get tokensDoNotSupportSynchronization => 'Následující tokeny nepodporují synchronizaci a musí být znovu zaregistrovány:'; @override - String errorRollOutFailed(Object name, Object errorCode) { - return 'Registrace tokenu $name selhala. Kód chyby: $errorCode'; + String errorRollOutFailed(Object name) { + return 'Registrace tokenu $name selhala.'; + } + + @override + String statusCode(Object statusCode) { + return 'Stavový kód: $statusCode'; } @override @@ -156,7 +161,16 @@ class AppLocalizationsCs extends AppLocalizations { String get unexpectedError => 'Nastala neočekávaná chyba.'; @override - String get pollingFailNoNetworkConnection => 'Stahování selhalo. Server není dostupný.'; + String get pollingFailed => 'Dotaz se nezdařil.'; + + @override + String get noNetworkConnection => 'No network connection.'; + + @override + String get serverNotReachable => 'Na server se nepodařilo dovolat.'; + + @override + String get couldNotSignMessage => 'Zprávu se nepodařilo podepsat.'; @override String get useDeviceLocaleTitle => 'Použít jazyk zařízení'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 1f6dd588b..236fd1863 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -129,8 +129,13 @@ class AppLocalizationsDe extends AppLocalizations { String get tokensDoNotSupportSynchronization => 'Die folgenden Token unterstützen keine Synchronisation und müssen erneut ausgerollt werden:'; @override - String errorRollOutFailed(Object name, Object errorCode) { - return 'Ausrollen von $name ist fehlgeschlagen. Fehlercode: $errorCode'; + String errorRollOutFailed(Object name) { + return 'Ausrollen von $name ist fehlgeschlagen.'; + } + + @override + String statusCode(Object statusCode) { + return 'Statuscode: $statusCode'; } @override @@ -156,7 +161,16 @@ class AppLocalizationsDe extends AppLocalizations { String get unexpectedError => 'Ein unerwarteter Fehler ist aufgetreten.'; @override - String get pollingFailNoNetworkConnection => 'Abfrage fehlgeschlagen, der Server ist nicht erreichbar.'; + String get pollingFailed => 'Abfrage fehlgeschlagen.'; + + @override + String get noNetworkConnection => 'No network connection.'; + + @override + String get serverNotReachable => 'Der Server konnte nicht erreicht werden.'; + + @override + String get couldNotSignMessage => 'Nachricht konnte nicht signiert werden.'; @override String get useDeviceLocaleTitle => 'Nutze Systemsprache'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index a33128ead..98460b884 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -129,8 +129,13 @@ class AppLocalizationsEn extends AppLocalizations { String get tokensDoNotSupportSynchronization => 'The following tokens do not support synchronization and must be rolled out again:'; @override - String errorRollOutFailed(Object name, Object errorCode) { - return 'Rolling out token $name failed.\nError code: $errorCode'; + String errorRollOutFailed(Object name) { + return 'Rolling out token $name failed.'; + } + + @override + String statusCode(Object statusCode) { + return 'Status code: $statusCode'; } @override @@ -156,7 +161,16 @@ class AppLocalizationsEn extends AppLocalizations { String get unexpectedError => 'An unexpected error occurred.'; @override - String get pollingFailNoNetworkConnection => 'Polling failed. Server cannot be reached.'; + String get pollingFailed => 'Polling failed.'; + + @override + String get noNetworkConnection => 'No network connection.'; + + @override + String get serverNotReachable => 'The server could not be reached.'; + + @override + String get couldNotSignMessage => 'Could not sign message.'; @override String get useDeviceLocaleTitle => 'Use device language'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 41882a394..48b543875 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -129,8 +129,13 @@ class AppLocalizationsEs extends AppLocalizations { String get tokensDoNotSupportSynchronization => 'Las siguientes tokens no admiten la sincronización y deben volver a desplegarse:'; @override - String errorRollOutFailed(Object name, Object errorCode) { - return 'Error en la extracción de el token $name. Código de error: $errorCode'; + String errorRollOutFailed(Object name) { + return 'Error en la extracción de el token $name.'; + } + + @override + String statusCode(Object statusCode) { + return 'Código de estado: $statusCode'; } @override @@ -156,7 +161,16 @@ class AppLocalizationsEs extends AppLocalizations { String get unexpectedError => 'Se ha producido un error inesperado.'; @override - String get pollingFailNoNetworkConnection => 'Error de sondeo. No se puede acceder al servidor.'; + String get pollingFailed => 'Consulta fallida.'; + + @override + String get noNetworkConnection => 'No network connection.'; + + @override + String get serverNotReachable => 'No se ha podido acceder al servidor.'; + + @override + String get couldNotSignMessage => 'No se ha podido firmar el mensaje.'; @override String get useDeviceLocaleTitle => 'Utiliza el idioma del teléfono'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 3412155d3..805ad05f9 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -129,8 +129,13 @@ class AppLocalizationsFr extends AppLocalizations { String get tokensDoNotSupportSynchronization => 'Ces jetons ne supportent pas la synchronisation et doivent être de nouveau générés:'; @override - String errorRollOutFailed(Object name, Object errorCode) { - return 'Le déploiement du jeton $name a échoué. Erreur réseau: $errorCode'; + String errorRollOutFailed(Object name) { + return 'Le déploiement du jeton $name a échoué.'; + } + + @override + String statusCode(Object statusCode) { + return 'Code d\'état : $statusCode'; } @override @@ -156,7 +161,16 @@ class AppLocalizationsFr extends AppLocalizations { String get unexpectedError => 'Une erreur inattendue s\'est produite.'; @override - String get pollingFailNoNetworkConnection => 'L\'interrogation a échoué. Le serveur est injoignable.'; + String get pollingFailed => 'Échec de la requête.'; + + @override + String get noNetworkConnection => 'No network connection.'; + + @override + String get serverNotReachable => 'Le serveur n\'a pas pu être atteint.'; + + @override + String get couldNotSignMessage => 'Impossible de signer le message.'; @override String get useDeviceLocaleTitle => 'Utiliser la langue de l\'appareil'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index be762b1fd..3cb3b0198 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -129,8 +129,13 @@ class AppLocalizationsNl extends AppLocalizations { String get tokensDoNotSupportSynchronization => 'Voor de volgende tokens wordt synchroniseren niet ondersteunt, ze moeten opnieuw worden aangeleverd:'; @override - String errorRollOutFailed(Object name, Object errorCode) { - return 'Uitrollen van token $name mislukt. Fout code: $errorCode'; + String errorRollOutFailed(Object name) { + return 'Uitrollen van token $name mislukt.'; + } + + @override + String statusCode(Object statusCode) { + return 'Statuscode: $statusCode'; } @override @@ -156,7 +161,16 @@ class AppLocalizationsNl extends AppLocalizations { String get unexpectedError => 'Er is een onverwachte fout opgetreden.'; @override - String get pollingFailNoNetworkConnection => 'Zoeken naar berichten mislukt. Server kan niet worden bereikt.'; + String get pollingFailed => 'Vraag mislukt.'; + + @override + String get noNetworkConnection => 'No network connection.'; + + @override + String get serverNotReachable => 'De server kon niet worden bereikt.'; + + @override + String get couldNotSignMessage => 'Bericht niet kunnen ondertekenen.'; @override String get useDeviceLocaleTitle => 'Gebruik de taal van het apparaat'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index a59cdc2a3..8362845ba 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -129,8 +129,13 @@ class AppLocalizationsPl extends AppLocalizations { String get tokensDoNotSupportSynchronization => 'Następujące tokeny nie wspierają synchronizacji i muszą zostać wdrożone od nowa:'; @override - String errorRollOutFailed(Object name, Object errorCode) { - return 'Wdrażanie tokenu $name nieudane. Kod błędu: $errorCode'; + String errorRollOutFailed(Object name) { + return 'Wdrażanie tokenu $name nieudane.'; + } + + @override + String statusCode(Object statusCode) { + return 'Kod statusu: $statusCode'; } @override @@ -156,7 +161,16 @@ class AppLocalizationsPl extends AppLocalizations { String get unexpectedError => 'Wystąpił nieoczekiwany błąd.'; @override - String get pollingFailNoNetworkConnection => 'Serwer jest nieosiągalny.'; + String get pollingFailed => 'Zapytanie nie powiodło się.'; + + @override + String get noNetworkConnection => 'No network connection.'; + + @override + String get serverNotReachable => 'Nie można uzyskać połączenia z serwerem.'; + + @override + String get couldNotSignMessage => 'Nie można podpisać wiadomości.'; @override String get useDeviceLocaleTitle => 'Użyj języka urządzenia.'; @@ -269,7 +283,7 @@ class AppLocalizationsPl extends AppLocalizations { String get noLogToSend => 'There is log to send.'; @override - String get errorMailBody => 'The error log file is attached.\nYou can replace this text with additional information about the error.'; + String get errorMailBody => 'Plik dziennika błędów jest dołączony.\nTekst ten można zastąpić dodatkowymi informacjami o błędzie.'; @override String get errorLogCleared => 'Wyczyszczono dzienniki błędów'; diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 708729a1d..1cc9dae60 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -174,15 +174,21 @@ "@tokensDoNotSupportSynchronization": { "description": "Informs the user that the following tokens cannot be synchronized as they do not support that." }, - "errorRollOutFailed": "Uitrollen van token {name} mislukt. Fout code: {errorCode}", + "errorRollOutFailed": "Uitrollen van token {name} mislukt.", "@errorRollOutFailed": { "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "placeholders": { "name": { "example": "PUSH1234A" - }, - "errorCode": { - "example": "500" + } + } + }, + "statusCode": "Statuscode: {statusCode}", + "@statusCode": { + "description": "Tells the user the status code of the error.", + "placeholders": { + "statusCode": { + "example": "400" } } }, @@ -212,9 +218,21 @@ "@unexpectedError": { "description": "Title of page report mode." }, - "pollingFailNoNetworkConnection": "Zoeken naar berichten mislukt. Server kan niet worden bereikt.", - "@pollingFailNoNetworkConnection": { - "description": "Tells the user that the roll-out failed because no network connection is available." + "pollingFailed": "Vraag mislukt.", + "@pollingFailed": { + "description": "Tells the user that the polling failed." + }, + "noNetworkConnection": "No network connection.", + "@noNetworkConnection": { + "description": "Tells the user that there is no network connection." + }, + "serverNotReachable": "De server kon niet worden bereikt.", + "@serverNotReachable": { + "description": "Tells the user that the server could not be reached." + }, + "couldNotSignMessage": "Bericht niet kunnen ondertekenen.", + "@couldNotSignMessage": { + "description": "Tells the user that the message could not be signed." }, "useDeviceLocaleTitle": "Gebruik de taal van het apparaat", "@useDeviceLocaleTitle": { diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index d8979d3ff..10deb039c 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -176,16 +176,22 @@ "@tokensDoNotSupportSynchronization": { "description": "Informs the user that the following tokens cannot be synchronized as they do not support that." }, - "errorRollOutFailed": "Wdrażanie tokenu {name} nieudane. Kod błędu: {errorCode}", + "errorRollOutFailed": "Wdrażanie tokenu {name} nieudane.", "@errorRollOutFailed": { "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "type": "text", "placeholders": { "name": { "example": "PUSH1234A" - }, - "errorCode": { - "example": "500" + } + } + }, + "statusCode": "Kod statusu: {statusCode}", + "@statusCode": { + "description": "Tells the user the status code of the error.", + "placeholders": { + "statusCode": { + "example": "400" } } }, @@ -215,9 +221,21 @@ "@unexpectedError": { "description": "Title of page report mode." }, - "pollingFailNoNetworkConnection": "Serwer jest nieosiągalny.", - "@pollingFailNoNetworkConnection": { - "description": "Tells the user that the roll-out failed because no network connection is available." + "pollingFailed": "Zapytanie nie powiodło się.", + "@pollingFailed": { + "description": "Tells the user that the polling failed." + }, + "noNetworkConnection": "No network connection.", + "@noNetworkConnection": { + "description": "Tells the user that there is no network connection." + }, + "serverNotReachable": "Nie można uzyskać połączenia z serwerem.", + "@serverNotReachable": { + "description": "Tells the user that the server could not be reached." + }, + "couldNotSignMessage": "Nie można podpisać wiadomości.", + "@couldNotSignMessage": { + "description": "Tells the user that the message could not be signed." }, "useDeviceLocaleTitle": "Użyj języka urządzenia.", "@useDeviceLocaleTitle": { @@ -328,7 +346,7 @@ "sendErrorDialogHeader": "Wyślij przez e-mail", "ok": "Ok", "noLogToSend": "There is log to send.", - "errorLogFileAttached": "Plik dziennika błędów jest dołączony.\nTekst ten można zastąpić dodatkowymi informacjami o błędzie.", + "errorMailBody": "Plik dziennika błędów jest dołączony.\nTekst ten można zastąpić dodatkowymi informacjami o błędzie.", "@errorMailBody": { "description": "Message for email body" }, diff --git a/lib/main_netknights.dart b/lib/main_netknights.dart index 05f2621c0..6fb98ec99 100644 --- a/lib/main_netknights.dart +++ b/lib/main_netknights.dart @@ -57,7 +57,7 @@ class PrivacyIDEAAuthenticator extends ConsumerWidget { globalRef = ref; final locale = ref.watch(settingsProvider).currentLocale; return MaterialApp( - debugShowCheckedModeBanner: true, + debugShowCheckedModeBanner: false, navigatorKey: globalNavigatorKey, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, diff --git a/lib/state_notifiers/token_notifier.dart b/lib/state_notifiers/token_notifier.dart index 636b4effd..824bf7f97 100644 --- a/lib/state_notifiers/token_notifier.dart +++ b/lib/state_notifiers/token_notifier.dart @@ -35,7 +35,8 @@ import '../utils/view_utils.dart'; import '../widgets/two_step_dialog.dart'; class TokenNotifier extends StateNotifier { - Future? isLoading; + late Future isInitialized; + Future isLoading = Future.value(); final TokenRepository _repo; final QrParser _qrParser; final RsaUtils _rsaUtils; @@ -60,7 +61,14 @@ class TokenNotifier extends StateNotifier { super( initialState ?? TokenState(), ) { - loadFromRepo(); + _init(); + } + + void _init() async { + isInitialized = Future(() async { + await loadFromRepo(); + return true; + }); } void _saveOrReplaceTokensRepo(List tokens) async { @@ -96,6 +104,7 @@ class TokenNotifier extends StateNotifier { checkNotificationPermission(); } }); + await isLoading; } catch (_) { return false; } @@ -312,10 +321,11 @@ class TokenNotifier extends StateNotifier { } if (token.expirationDate?.isBefore(DateTime.now()) == true) { Logger.info('Ignoring rollout request: Token "${token.id}" is expired. ', name: 'token_notifier.dart#rolloutPushToken'); + if (globalNavigatorKey.currentContext != null) { - showMessage( - message: AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutTokenExpired(token.label), - duration: const Duration(seconds: 3), + globalRef?.read(statusMessageProvider.notifier).state = ( + AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutTokenExpired(token.label), + null, ); } removeToken(token); @@ -382,15 +392,16 @@ class TokenNotifier extends StateNotifier { message = response.body.isNotEmpty ? (json.decode(response.body)['result']?['error']?['message']) : null; message = message != null ? '\n$message' : ''; showMessage( - message: AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutFailed(token.label, response.statusCode) + message, + message: AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutFailed(token.label) + message, duration: const Duration(seconds: 3), ); - } on FormatException catch (e) { + } on FormatException { // Format Exception is thrown if the response body is not a valid json. This happens if the server is not reachable. - if (globalRef != null) { - globalRef!.read(statusMessageProvider.notifier).state = - (AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutFailed(token.label, response.statusCode), null); - } + + globalRef?.read(statusMessageProvider.notifier).state = ( + AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutFailed(token.label), + AppLocalizations.of(globalNavigatorKey.currentContext!)!.statusCode(response.statusCode) + ); } updateToken(token, (p0) => p0.copyWith(rolloutState: PushTokenRollOutState.sendRSAPublicKeyFailed)); diff --git a/lib/utils/push_provider.dart b/lib/utils/push_provider.dart index 162200712..c582d0509 100644 --- a/lib/utils/push_provider.dart +++ b/lib/utils/push_provider.dart @@ -237,11 +237,15 @@ class PushProvider { return; } - Future pollForChallenges({bool showMessageForEachToken = false}) async { + Future pollForChallenges() async { final connectivityResult = await (Connectivity().checkConnectivity()); if (connectivityResult == ConnectivityResult.none) { Logger.info('Tried to poll without any internet connection available.', name: 'push_provider.dart#pollForChallenges'); - return AppLocalizations.of(globalNavigatorKey.currentContext!)!.pollingFailNoNetworkConnection; + globalRef?.read(statusMessageProvider.notifier).state = ( + AppLocalizations.of(globalNavigatorKey.currentContext!)!.pollingFailed, + AppLocalizations.of(globalNavigatorKey.currentContext!)!.noNetworkConnection, + ); + return; } // Get all push tokens @@ -251,24 +255,18 @@ class PushProvider { if (pushTokens.isEmpty && globalRef?.read(settingsProvider).enablePolling == true) { Logger.info('No push token is available for polling, polling is disabled.', name: 'push_provider.dart#pollForChallenges'); globalRef?.read(settingsProvider.notifier).setPolling(false); - return null; + return; } // Start request for each token Logger.info('Polling for challenges: ${pushTokens.length} Tokens', name: 'push_provider.dart#pollForChallenges'); for (PushToken p in pushTokens) { - pollForChallenge(p).then((errorMessage) { - if (errorMessage != null && showMessageForEachToken) { - Logger.warning(errorMessage, name: 'push_provider.dart#pollForChallenges'); - // TODO: Improve error message - showMessage(message: errorMessage); - } - }); + pollForChallenge(p); } - return null; + return; } - Future pollForChallenge(PushToken token) async { + Future pollForChallenge(PushToken token) async { String timestamp = DateTime.now().toUtc().toIso8601String(); String message = '${token.serial}|$timestamp'; @@ -277,8 +275,12 @@ class PushProvider { Logger.info(rsaUtils.runtimeType.toString(), name: 'push_provider.dart#pollForChallenge'); String? signature = await rsaUtils.trySignWithToken(token, message); if (signature == null) { + globalRef?.read(statusMessageProvider.notifier).state = ( + AppLocalizations.of(globalNavigatorKey.currentContext!)!.pollingFailed, + AppLocalizations.of(globalNavigatorKey.currentContext!)!.couldNotSignMessage, + ); Logger.warning('Polling push tokens failed because signing the message failed.', name: 'push_provider.dart#pollForChallenge'); - return null; + return; } Map parameters = { 'serial': token.serial, @@ -305,18 +307,31 @@ class PushProvider { break; case 403: + final error = getErrorMessageFromResponse(response); + globalRef?.read(statusMessageProvider.notifier).state = ( + AppLocalizations.of(globalNavigatorKey.currentContext!)!.pollingFailed, + error ?? AppLocalizations.of(globalNavigatorKey.currentContext!)!.statusCode(response.statusCode), + ); Logger.warning('Polling push token failed with status code ${response.statusCode}', name: 'push_provider.dart#pollForChallenge', error: getErrorMessageFromResponse(response)); - return null; + return; default: - var error = getErrorMessageFromResponse(response); - return "${AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorWhenPullingChallenges(token.serial)}\n$error"; + final error = getErrorMessageFromResponse(response); + globalRef?.read(statusMessageProvider.notifier).state = ( + AppLocalizations.of(globalNavigatorKey.currentContext!)!.pollingFailed, + error ?? AppLocalizations.of(globalNavigatorKey.currentContext!)!.statusCode(response.statusCode), + ); + return; } } catch (e) { - return "${AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorWhenPullingChallenges(token.serial)}\n${e.toString()}"; + globalRef?.read(statusMessageProvider.notifier).state = ( + AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorWhenPullingChallenges(token.serial), + null, + ); + return; } - return null; + return; } /// Checks if the firebase token was changed and updates it if necessary. diff --git a/lib/utils/riverpod_providers.dart b/lib/utils/riverpod_providers.dart index 1b453e0a1..d50119559 100644 --- a/lib/utils/riverpod_providers.dart +++ b/lib/utils/riverpod_providers.dart @@ -1,9 +1,6 @@ -import 'dart:developer'; - import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; - import '../model/mixins/sortable_mixin.dart'; import '../model/push_request.dart'; import '../model/states/app_state.dart'; @@ -133,13 +130,7 @@ final connectivityProvider = StreamProvider((ref) { }); final statusMessageProvider = StateProvider<(String, String?)?>((ref) { - Logger.info("New connectionStateProvider created"); - final next = ref.watch(connectivityProvider).asData?.value; - if (next == null || next == ConnectivityResult.none) { - log("ConnectionState is $next"); - return ('No Connection', null); - } - log("ConnectionState is $next"); + Logger.info("New statusMessageProvider created"); return null; }); diff --git a/lib/utils/text_size.dart b/lib/utils/text_size.dart index 70ffaabf6..46ed8c0f3 100644 --- a/lib/utils/text_size.dart +++ b/lib/utils/text_size.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -Size textSizeOf(String text, TextStyle style) { - final TextPainter textPainter = TextPainter(text: TextSpan(text: text, style: style), maxLines: 1, textDirection: TextDirection.ltr) - ..layout(minWidth: 0, maxWidth: double.infinity); +Size textSizeOf(String text, TextStyle style, {int? maxLines = 1, double minWidth = 0, double maxWidth = double.infinity}) { + final TextPainter textPainter = TextPainter(text: TextSpan(text: text, style: style), maxLines: maxLines, textDirection: TextDirection.ltr) + ..layout(minWidth: minWidth, maxWidth: maxWidth); return textPainter.size; } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 61b81e27a..e399156de 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -124,71 +124,5 @@ String? getErrorMessageFromResponse(Response response) { } catch (e) { errorMessage = null; } - if (errorMessage == null) { - final statusMessage = _statusMessageFromCode[response.statusCode]; - if (statusMessage != null) { - errorMessage = '${response.statusCode}: $statusMessage'; - } else { - errorMessage = 'Status Code: ${response.statusCode}'; - } - } return errorMessage; } - -Map _statusMessageFromCode = { - 100: "Continue", - 101: "Switching Protocols", - 102: "Processing", - 200: "OK", - 201: "Created", - 202: "Accepted", - 203: "Non Authoritative Information", - 204: "No Content", - 205: "Reset Content", - 206: "Partial Content", - 207: "Multi-Status", - 300: "Multiple Choices", - 301: "Moved Permanently", - 302: "Moved Temporarily", - 303: "See Other", - 304: "Not Modified", - 305: "Use Proxy", - 307: "Temporary Redirect", - 308: "Permanent Redirect", - 400: "Bad Request", - 401: "Unauthorized", - 402: "Payment Required", - 403: "Forbidden", - 404: "Not Found", - 405: "Method Not Allowed", - 406: "Not Acceptable", - 407: "Proxy Authentication Required", - 408: "Request Timeout", - 409: "Conflict", - 410: "Gone", - 411: "Length Required", - 412: "Precondition Failed", - 413: "Request Entity Too Large", - 414: "Request-URI Too Long", - 415: "Unsupported Media Type", - 416: "Requested Range Not Satisfiable", - 417: "Expectation Failed", - 418: "I'm a teapot", - 419: "Insufficient Space on Resource", - 420: "Method Failure", - 422: "Unprocessable Entity", - 423: "Locked", - 424: "Failed Dependency", - 428: "Precondition Required", - 429: "Too Many Requests", - 431: "Request Header Fields Too Large", - 451: "Unavailable For Legal Reasons", - 500: "Internal Server Error", - 501: "Not Implemented", - 502: "Bad Gateway", - 503: "Service Unavailable", - 504: "Gateway Timeout", - 505: "HTTP Version Not Supported", - 507: "Insufficient Storage", - 511: "Network Authentication Required" -}; diff --git a/lib/views/main_view/main_view.dart b/lib/views/main_view/main_view.dart index e6463dcad..328fcd00d 100644 --- a/lib/views/main_view/main_view.dart +++ b/lib/views/main_view/main_view.dart @@ -5,6 +5,7 @@ import 'package:flutterlifecyclehooks/flutterlifecyclehooks.dart'; import '../../model/states/app_state.dart'; import '../../utils/logger.dart'; import '../../utils/riverpod_providers.dart'; +import '../../widgets/app_wrapper.dart'; import '../../widgets/connectivity_status_bar.dart'; import 'main_view_widgets/main_view_navigation_bar.dart'; import 'main_view_widgets/main_view_tokens_list.dart'; @@ -54,13 +55,15 @@ class _MainViewState extends ConsumerState with LifecycleMixin { child: widget.appIcon, ), ), - body: ConnectivityStatusBar( - child: Stack( - clipBehavior: Clip.antiAliasWithSaveLayer, - children: [ - MainViewTokensList(nestedScrollViewKey: globalKey), - const MainViewNavigationBar(), - ], + body: ConnectivityListener( + child: ConnectivityStatusBar( + child: Stack( + clipBehavior: Clip.antiAliasWithSaveLayer, + children: [ + MainViewTokensList(nestedScrollViewKey: globalKey), + const MainViewNavigationBar(), + ], + ), ), ), ); diff --git a/lib/views/main_view/main_view_widgets/main_view_tokens_list.dart b/lib/views/main_view/main_view_widgets/main_view_tokens_list.dart index 9125dffe2..14bc38456 100644 --- a/lib/views/main_view/main_view_widgets/main_view_tokens_list.dart +++ b/lib/views/main_view/main_view_widgets/main_view_tokens_list.dart @@ -54,8 +54,7 @@ class _MainViewTokensListState extends ConsumerState { message: AppLocalizations.of(context)!.pollingChallenges, duration: const Duration(seconds: 1), ); - final errorMessage = await PushProvider().pollForChallenges(showMessageForEachToken: true); - if (errorMessage != null) showMessage(message: errorMessage); + await PushProvider().pollForChallenges(); }, child: SlidableAutoCloseBehavior( child: DragItemScroller( diff --git a/lib/views/push_token_view/widgets/push_tokens_view_list.dart b/lib/views/push_token_view/widgets/push_tokens_view_list.dart index 84f20ef4d..1c5b7288d 100644 --- a/lib/views/push_token_view/widgets/push_tokens_view_list.dart +++ b/lib/views/push_token_view/widgets/push_tokens_view_list.dart @@ -45,8 +45,7 @@ class _PushTokensViwListState extends ConsumerState { message: AppLocalizations.of(context)!.pollingChallenges, duration: const Duration(seconds: 1), ); - final errorMessage = await PushProvider().pollForChallenges(showMessageForEachToken: true); - if (errorMessage != null) showMessage(message: errorMessage); + await PushProvider().pollForChallenges(); }, child: SlidableAutoCloseBehavior( child: DragItemScroller( diff --git a/lib/widgets/app_wrapper.dart b/lib/widgets/app_wrapper.dart index 2ea8b9fd8..1d165bc42 100644 --- a/lib/widgets/app_wrapper.dart +++ b/lib/widgets/app_wrapper.dart @@ -1,7 +1,11 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:easy_dynamic_theme/easy_dynamic_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../l10n/app_localizations.dart'; +import '../utils/riverpod_providers.dart'; + class AppWrapper extends StatelessWidget { final Widget child; @@ -14,3 +18,23 @@ class AppWrapper extends StatelessWidget { ); } } + +class ConnectivityListener extends ConsumerWidget { + final Widget child; + const ConnectivityListener({required this.child, super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final connectivity = ref.watch(connectivityProvider).asData?.value; + final hasConnection = connectivity != null && connectivity != ConnectivityResult.none; + ref.read(tokenProvider.notifier).isInitialized.then( + (value) { + final hasPushTokens = ref.read(tokenProvider).hasPushTokens; + if (!hasConnection && hasPushTokens) { + ref.read(statusMessageProvider.notifier).state = (AppLocalizations.of(context)!.noNetworkConnection, null); + } + }, + ); + return child; + } +} diff --git a/lib/widgets/connectivity_status_bar.dart b/lib/widgets/connectivity_status_bar.dart index 39b7e0623..0caf18bcb 100644 --- a/lib/widgets/connectivity_status_bar.dart +++ b/lib/widgets/connectivity_status_bar.dart @@ -1,5 +1,4 @@ import 'dart:collection'; -import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -18,6 +17,7 @@ class ConnectivityStatusBar extends ConsumerStatefulWidget { class _ConnectivityStatusBarState extends ConsumerState { (String, String?)? previousStatusMessage; (String, String?)? currentStatusMessage; + Queue<(String, String?)> statusbarQueue = Queue(); late Function(DismissDirection) onDismissed; @@ -27,10 +27,10 @@ class _ConnectivityStatusBarState extends ConsumerState { @override Widget build(BuildContext context) { final newStatusMessage = ref.watch(statusMessageProvider); - if (newStatusMessage != previousStatusMessage && newStatusMessage != currentStatusMessage) { - previousStatusMessage = newStatusMessage; - _addToQueueIfNotInQueue(newStatusMessage); - } + // if (newStatusMessage != previousStatusMessage && newStatusMessage != currentStatusMessage) { + // previousStatusMessage = newStatusMessage; + _addToQueueIfNotInQueue(newStatusMessage); + // } return widget.child; } @@ -41,7 +41,7 @@ class _ConnectivityStatusBarState extends ConsumerState { currentStatusMessage = null; statusbarOverlay!.remove(); statusbarOverlay = null; - log("Removing statusbar overlay"); + ref.read(statusMessageProvider.notifier).state = null; _tryPop(); }); }; @@ -49,8 +49,6 @@ class _ConnectivityStatusBarState extends ConsumerState { super.initState(); } - Queue<(String, String?)> statusbarQueue = Queue(); - void _addToQueueIfNotInQueue((String, String?)? statusMessage) { if (statusMessage == null) return; if (!statusbarQueue.contains(statusMessage) && currentStatusMessage != statusMessage) { @@ -74,9 +72,13 @@ class _ConnectivityStatusBarState extends ConsumerState { statusbarOverlay = null; } - statusbarOverlay = - OverlayEntry(builder: (context) => StatusBarOverlayEntry(onDismissed: onDismissed, statusText: statusText, statusSubText: statusSubText)); - log("Showing statusbar overlay"); + statusbarOverlay = OverlayEntry( + builder: (context) => StatusBarOverlayEntry( + onDismissed: onDismissed, + statusText: statusText, + statusSubText: statusSubText, + ), + ); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { Overlay.of(context).insert(statusbarOverlay!); }); @@ -95,46 +97,139 @@ class StatusBarOverlayEntry extends StatefulWidget { } class _StatusBarOverlayEntryState extends State with SingleTickerProviderStateMixin { - bool isFirstFrame = false; + bool isFirstFrame = true; + static const double margin = 10; + static const double padding = 10; + static const Duration showDuration = Duration(seconds: 5); + late AnimationController autoDismissAnimationController; + late Animation autoDismissAnimation; + late Function(DismissDirection) onDismissed; @override void initState() { + autoDismissAnimationController = AnimationController(vsync: this, duration: showDuration); + final curvedAnimation = CurvedAnimation(parent: autoDismissAnimationController, curve: Curves.easeOut); + autoDismissAnimation = Tween(begin: 1, end: 0).animate(curvedAnimation) + ..addListener(() { + setState(() {}); + }); + autoDismissAnimation.addListener(() { + if (mounted) { + setState(() {}); + if (autoDismissAnimation.isCompleted) { + onDismissed(DismissDirection.endToStart); + } + } + }); + + onDismissed = (DismissDirection direction) { + autoDismissAnimationController.stop(); + widget.onDismissed(direction); + }; + super.initState(); } @override Widget build(BuildContext context) { - if (!isFirstFrame) { + if (isFirstFrame) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - setState(() { - isFirstFrame = true; - }); + if (mounted) { + setState(() { + isFirstFrame = false; + }); + } }); } + + final maxWidth = MediaQuery.of(context).size.width - margin * 2 - padding * 2; + final statusTextStyle = Theme.of(context).textTheme.bodyLarge?.copyWith(color: Colors.white) ?? const TextStyle(); + final statusSubTextStyle = Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.white) ?? const TextStyle(); + final statusTextHeight = textSizeOf(widget.statusText, statusTextStyle, maxWidth: maxWidth).height; + final statusSubTextHeight = widget.statusSubText != null ? textSizeOf(widget.statusSubText!, statusSubTextStyle, maxWidth: maxWidth).height : 0; return AnimatedPositioned( - top: isFirstFrame ? 30 : -textSizeOf(widget.statusText, Theme.of(context).textTheme.bodyLarge!).height - 10, - left: 10, - right: 10, + onEnd: () { + if (mounted) { + setState(() { + autoDismissAnimationController.forward(); + }); + } + }, + top: isFirstFrame ? -statusTextHeight - statusSubTextHeight - 10 : 30, + left: margin, + right: margin, curve: Curves.easeOut, duration: const Duration(milliseconds: 250), child: Material( color: Colors.transparent, - child: Dismissible( - onDismissed: (direction) { - widget.onDismissed(direction); + child: GestureDetector( + onTap: () { + if (mounted) { + if (autoDismissAnimationController.isAnimating) { + autoDismissAnimationController.stop(); + } else { + autoDismissAnimationController.forward(); + } + } }, - key: const Key('statusbarOverlay'), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - color: Theme.of(context).colorScheme.error, - ), - padding: const EdgeInsets.all(10.0), - child: Center( - child: Text( - widget.statusText, - style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Colors.white), - ), + child: Dismissible( + onDismissed: (direction) { + if (mounted) { + onDismissed(direction); + } + }, + key: const Key('statusbarOverlay'), + child: Stack( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(padding), + color: Theme.of(context).colorScheme.error, + ), + padding: const EdgeInsets.all(padding), + child: Center( + child: LayoutBuilder(builder: (context, constraints) { + return Column( + children: [ + SizedBox( + width: maxWidth, + child: Center( + child: Text( + widget.statusText, + style: statusTextStyle, + ), + ), + ), + if (widget.statusSubText != null) + SizedBox( + width: maxWidth, + child: Center( + child: Text( + widget.statusSubText!, + style: statusSubTextStyle, + ), + ), + ), + ], + ); + }), + ), + ), + Positioned.fill( + child: Align( + alignment: AlignmentDirectional.topStart, + child: Container( + margin: const EdgeInsets.symmetric(horizontal: padding / 3 * 2), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(1.5), + color: Theme.of(context).colorScheme.primary, + ), + height: 3, + width: autoDismissAnimation.value * (maxWidth + padding / 3 * 2), + ), + ), + ) + ], ), ), ), diff --git a/lib/widgets/two_step_dialog.dart b/lib/widgets/two_step_dialog.dart index 11f8819a6..118cbb301 100644 --- a/lib/widgets/two_step_dialog.dart +++ b/lib/widgets/two_step_dialog.dart @@ -18,7 +18,6 @@ limitations under the License. */ -import 'dart:developer'; import 'dart:typed_data'; import 'dart:ui'; @@ -29,6 +28,7 @@ import 'package:privacyidea_authenticator/utils/utils.dart'; import 'package:privacyidea_authenticator/utils/view_utils.dart'; import 'package:privacyidea_authenticator/widgets/default_dialog.dart'; +import '../utils/logger.dart'; import 'widget_keys.dart'; class GenerateTwoStepDialog extends StatelessWidget { @@ -57,7 +57,7 @@ class GenerateTwoStepDialog extends StatelessWidget { String phoneChecksum = await generatePhoneChecksum(phonePart: salt); if (!context.mounted) { - log('GenerateTwoStepDialog: context is not mounted anymore. Aborting.'); + Logger.warning('GenerateTwoStepDialog: context is not mounted anymore. Aborting.'); return; } diff --git a/test/unit_test/state_notifiers/push_request_notifier_test.dart b/test/unit_test/state_notifiers/push_request_notifier_test.dart index e6adbed70..bc38fe6ad 100644 --- a/test/unit_test/state_notifiers/push_request_notifier_test.dart +++ b/test/unit_test/state_notifiers/push_request_notifier_test.dart @@ -58,7 +58,6 @@ void _testPushRequestNotifier() { id: 1, expirationDate: DateTime.now().add(const Duration(minutes: 10)), ); - log('1'); mockPushProvider.simulatePush(pr); expect(container.read(testProvider), pr); }); From ff9d27653621db17fe8eedbcba25cdbdc1b180bb Mon Sep 17 00:00:00 2001 From: Frank Merkel <138444693+frankmer@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:11:03 +0100 Subject: [PATCH 3/8] localization --- lib/l10n/app_cs.arb | 2 +- lib/l10n/app_de.arb | 2 +- lib/l10n/app_es.arb | 2 +- lib/l10n/app_fr.arb | 2 +- lib/l10n/app_nl.arb | 2 +- lib/l10n/app_pl.arb | 2 +- .../push_token_widgets/rollout_failed_widget.dart | 15 +++++++++------ lib/widgets/press_button.dart | 2 +- 8 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/l10n/app_cs.arb b/lib/l10n/app_cs.arb index 4e82acd9d..4342e66a6 100644 --- a/lib/l10n/app_cs.arb +++ b/lib/l10n/app_cs.arb @@ -229,7 +229,7 @@ "@pollingFailed": { "description": "Tells the user that the polling failed." }, - "noNetworkConnection": "No network connection.", + "noNetworkConnection": "Žádné připojení k síti.", "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index b146273c1..7054fed98 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -222,7 +222,7 @@ "@pollingFailed": { "description": "Tells the user that the polling failed." }, - "noNetworkConnection": "No network connection.", + "noNetworkConnection": "Keine Netzwerkverbindung.", "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 74a67c618..9b6bacbe6 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -225,7 +225,7 @@ "@pollingFailed": { "description": "Tells the user that the polling failed." }, - "noNetworkConnection": "No network connection.", + "noNetworkConnection": "No hay conexión a la red.", "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index c3fb4cd05..3f75cb1b9 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -229,7 +229,7 @@ "@pollingFailed": { "description": "Tells the user that the polling failed." }, - "noNetworkConnection": "No network connection.", + "noNetworkConnection": "Pas de connexion au réseau.", "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 1cc9dae60..208e94581 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -222,7 +222,7 @@ "@pollingFailed": { "description": "Tells the user that the polling failed." }, - "noNetworkConnection": "No network connection.", + "noNetworkConnection": "Geen netwerkverbinding.", "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 10deb039c..abbc54709 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -225,7 +225,7 @@ "@pollingFailed": { "description": "Tells the user that the polling failed." }, - "noNetworkConnection": "No network connection.", + "noNetworkConnection": "Brak połączenia sieciowego.", "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, diff --git a/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/rollout_failed_widget.dart b/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/rollout_failed_widget.dart index 868c4dee4..65128e16c 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/rollout_failed_widget.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/rollout_failed_widget.dart @@ -20,12 +20,15 @@ class RolloutFailedWidget extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - rolloutMsg(token.rolloutState, context), - style: Theme.of(context).textTheme.titleMedium, - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: false, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 6.0), + child: FittedBox( + child: Text( + rolloutMsg(token.rolloutState, context), + style: Theme.of(context).textTheme.titleMedium, + textAlign: TextAlign.center, + ), + ), ), Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/widgets/press_button.dart b/lib/widgets/press_button.dart index 8dd0403d4..4faf07756 100644 --- a/lib/widgets/press_button.dart +++ b/lib/widgets/press_button.dart @@ -36,7 +36,7 @@ class _PressButtonState extends State { return ElevatedButton( onPressed: isPressable ? press : null, style: widget.style, - child: widget.child, + child: FittedBox(child: widget.child), ); } } From 812179c56f627f68fe602cd252e4f3bdcec82d20 Mon Sep 17 00:00:00 2001 From: Frank Merkel <138444693+frankmer@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:35:47 +0100 Subject: [PATCH 4/8] some other translations --- lib/l10n/app_cs.arb | 8 ++++++++ lib/l10n/app_de.arb | 8 ++++++++ lib/l10n/app_en.arb | 8 ++++++++ lib/l10n/app_es.arb | 8 ++++++++ lib/l10n/app_fr.arb | 8 ++++++++ lib/l10n/app_localizations.dart | 12 ++++++++++++ lib/l10n/app_localizations_cs.dart | 8 +++++++- lib/l10n/app_localizations_de.dart | 8 +++++++- lib/l10n/app_localizations_en.dart | 6 ++++++ lib/l10n/app_localizations_es.dart | 8 +++++++- lib/l10n/app_localizations_fr.dart | 8 +++++++- lib/l10n/app_localizations_nl.dart | 8 +++++++- lib/l10n/app_localizations_pl.dart | 8 +++++++- lib/l10n/app_nl.arb | 8 ++++++++ lib/l10n/app_pl.arb | 8 ++++++++ lib/utils/network_utils.dart | 19 +++++++++++++++---- 16 files changed, 131 insertions(+), 10 deletions(-) diff --git a/lib/l10n/app_cs.arb b/lib/l10n/app_cs.arb index 4342e66a6..a2c20dcc3 100644 --- a/lib/l10n/app_cs.arb +++ b/lib/l10n/app_cs.arb @@ -233,6 +233,14 @@ "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, + "connectionFailed": "Připojení se nezdařilo.", + "@connectionFailed": { + "description": "Tells the user that the connection failed." + }, + "checkYourNetwork": "Zkontrolujte prosím síťové připojení a zkuste to znovu.", + "@checkYourNetwork": { + "description": "Tells the user to check the network connection." + }, "serverNotReachable": "Na server se nepodařilo dovolat.", "@serverNotReachable": { "description": "Tells the user that the server could not be reached." diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 7054fed98..c1afeb584 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -226,6 +226,14 @@ "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, + "connectionFailed": "Verbindung fehlgeschlagen.", + "@connectionFailed": { + "description": "Tells the user that the connection failed." + }, + "checkYourNetwork": "Bitte überprüfen Sie Ihre Netzwerkverbindung und versuchen Sie es erneut.", + "@checkYourNetwork": { + "description": "Tells the user to check the network connection." + }, "serverNotReachable": "Der Server konnte nicht erreicht werden.", "@serverNotReachable": { "description": "Tells the user that the server could not be reached." diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 6ac4fa5d1..cedeac939 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -225,6 +225,14 @@ "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, + "connectionFailed": "Connection failed.", + "@connectionFailed": { + "description": "Tells the user that the connection failed." + }, + "checkYourNetwork": "Please check your network connection and try again.", + "@checkYourNetwork": { + "description": "Tells the user to check the network connection." + }, "serverNotReachable": "The server could not be reached.", "@serverNotReachable": { "description": "Tells the user that the server could not be reached." diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 9b6bacbe6..06fd9afd6 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -229,6 +229,14 @@ "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, + "connectionFailed": "Conexión fallida.", + "@connectionFailed": { + "description": "Tells the user that the connection failed." + }, + "checkYourNetwork": "Compruebe su conexión de red e inténtelo de nuevo.", + "@checkYourNetwork": { + "description": "Tells the user to check the network connection." + }, "serverNotReachable": "No se ha podido acceder al servidor.", "@serverNotReachable": { "description": "Tells the user that the server could not be reached." diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 3f75cb1b9..5d2698768 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -233,6 +233,14 @@ "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, + "connectionFailed": "La connexion a échoué.", + "@connectionFailed": { + "description": "Tells the user that the connection failed." + }, + "checkYourNetwork": "Veuillez vérifier votre connexion réseau et réessayer.", + "@checkYourNetwork": { + "description": "Tells the user to check the network connection." + }, "serverNotReachable": "Le serveur n'a pas pu être atteint.", "@serverNotReachable": { "description": "Tells the user that the server could not be reached." diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 0d9d72e2b..0388f9745 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -403,6 +403,18 @@ abstract class AppLocalizations { /// **'No network connection.'** String get noNetworkConnection; + /// Tells the user that the connection failed. + /// + /// In en, this message translates to: + /// **'Connection failed.'** + String get connectionFailed; + + /// Tells the user to check the network connection. + /// + /// In en, this message translates to: + /// **'Please check your network connection and try again.'** + String get checkYourNetwork; + /// Tells the user that the server could not be reached. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_cs.dart b/lib/l10n/app_localizations_cs.dart index 5aeedc0c8..f439ac2da 100644 --- a/lib/l10n/app_localizations_cs.dart +++ b/lib/l10n/app_localizations_cs.dart @@ -164,7 +164,13 @@ class AppLocalizationsCs extends AppLocalizations { String get pollingFailed => 'Dotaz se nezdařil.'; @override - String get noNetworkConnection => 'No network connection.'; + String get noNetworkConnection => 'Žádné připojení k síti.'; + + @override + String get connectionFailed => 'Připojení se nezdařilo.'; + + @override + String get checkYourNetwork => 'Zkontrolujte prosím síťové připojení a zkuste to znovu.'; @override String get serverNotReachable => 'Na server se nepodařilo dovolat.'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 236fd1863..25db9a020 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -164,7 +164,13 @@ class AppLocalizationsDe extends AppLocalizations { String get pollingFailed => 'Abfrage fehlgeschlagen.'; @override - String get noNetworkConnection => 'No network connection.'; + String get noNetworkConnection => 'Keine Netzwerkverbindung.'; + + @override + String get connectionFailed => 'Verbindung fehlgeschlagen.'; + + @override + String get checkYourNetwork => 'Bitte überprüfen Sie Ihre Netzwerkverbindung und versuchen Sie es erneut.'; @override String get serverNotReachable => 'Der Server konnte nicht erreicht werden.'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 98460b884..f4d8f742b 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -166,6 +166,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get noNetworkConnection => 'No network connection.'; + @override + String get connectionFailed => 'Connection failed.'; + + @override + String get checkYourNetwork => 'Please check your network connection and try again.'; + @override String get serverNotReachable => 'The server could not be reached.'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 48b543875..10eaf2cb8 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -164,7 +164,13 @@ class AppLocalizationsEs extends AppLocalizations { String get pollingFailed => 'Consulta fallida.'; @override - String get noNetworkConnection => 'No network connection.'; + String get noNetworkConnection => 'No hay conexión a la red.'; + + @override + String get connectionFailed => 'Conexión fallida.'; + + @override + String get checkYourNetwork => 'Compruebe su conexión de red e inténtelo de nuevo.'; @override String get serverNotReachable => 'No se ha podido acceder al servidor.'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 805ad05f9..0ecbe2f40 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -164,7 +164,13 @@ class AppLocalizationsFr extends AppLocalizations { String get pollingFailed => 'Échec de la requête.'; @override - String get noNetworkConnection => 'No network connection.'; + String get noNetworkConnection => 'Pas de connexion au réseau.'; + + @override + String get connectionFailed => 'La connexion a échoué.'; + + @override + String get checkYourNetwork => 'Veuillez vérifier votre connexion réseau et réessayer.'; @override String get serverNotReachable => 'Le serveur n\'a pas pu être atteint.'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 3cb3b0198..704bf91b4 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -164,7 +164,13 @@ class AppLocalizationsNl extends AppLocalizations { String get pollingFailed => 'Vraag mislukt.'; @override - String get noNetworkConnection => 'No network connection.'; + String get noNetworkConnection => 'Geen netwerkverbinding.'; + + @override + String get connectionFailed => 'Verbinding mislukt.'; + + @override + String get checkYourNetwork => 'Controleer je netwerkverbinding en probeer het opnieuw.'; @override String get serverNotReachable => 'De server kon niet worden bereikt.'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 8362845ba..c48990312 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -164,7 +164,13 @@ class AppLocalizationsPl extends AppLocalizations { String get pollingFailed => 'Zapytanie nie powiodło się.'; @override - String get noNetworkConnection => 'No network connection.'; + String get noNetworkConnection => 'Brak połączenia sieciowego.'; + + @override + String get connectionFailed => 'Połączenie nie powiodło się.'; + + @override + String get checkYourNetwork => 'Sprawdź połączenie sieciowe i spróbuj ponownie.'; @override String get serverNotReachable => 'Nie można uzyskać połączenia z serwerem.'; diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 208e94581..aceb58897 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -226,6 +226,14 @@ "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, + "connectionFailed": "Verbinding mislukt.", + "@connectionFailed": { + "description": "Tells the user that the connection failed." + }, + "checkYourNetwork": "Controleer je netwerkverbinding en probeer het opnieuw.", + "@checkYourNetwork": { + "description": "Tells the user to check the network connection." + }, "serverNotReachable": "De server kon niet worden bereikt.", "@serverNotReachable": { "description": "Tells the user that the server could not be reached." diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index abbc54709..cf53d6e75 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -229,6 +229,14 @@ "@noNetworkConnection": { "description": "Tells the user that there is no network connection." }, + "connectionFailed": "Połączenie nie powiodło się.", + "@connectionFailed": { + "description": "Tells the user that the connection failed." + }, + "checkYourNetwork": "Sprawdź połączenie sieciowe i spróbuj ponownie.", + "@checkYourNetwork": { + "description": "Tells the user to check the network connection." + }, "serverNotReachable": "Nie można uzyskać połączenia z serwerem.", "@serverNotReachable": { "description": "Tells the user that the server could not be reached." diff --git a/lib/utils/network_utils.dart b/lib/utils/network_utils.dart index bb89fcbc8..6df313ee6 100644 --- a/lib/utils/network_utils.dart +++ b/lib/utils/network_utils.dart @@ -24,6 +24,8 @@ import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; import 'package:http/io_client.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:privacyidea_authenticator/l10n/app_localizations.dart'; +import 'package:privacyidea_authenticator/utils/customizations.dart'; import 'package:privacyidea_authenticator/utils/logger.dart'; import 'package:privacyidea_authenticator/utils/riverpod_providers.dart'; import 'package:privacyidea_authenticator/utils/view_utils.dart'; @@ -38,18 +40,27 @@ class PrivacyIdeaIOClient { if (kIsWeb) return; HttpClient httpClient = HttpClient(); httpClient.badCertificateCallback = ((X509Certificate cert, String host, int port) => !sslVerify); - httpClient.userAgent = 'privacyIDEA-App /' + httpClient.userAgent = 'privacyIDEA-App' + '/${(await PackageInfo.fromPlatform()).version}' ' ${Platform.operatingSystem}' - ' ${(await PackageInfo.fromPlatform()).version}'; + '/${Platform.operatingSystemVersion}'; IOClient ioClient = IOClient(httpClient); try { await ioClient.post(url, body: ''); } on SocketException { - globalRef?.read(statusMessageProvider.notifier).state = ('Connection failed', 'Please check your internet connection.'); + if (globalNavigatorKey.currentState?.context == null) return; + globalRef?.read(statusMessageProvider.notifier).state = ( + AppLocalizations.of(globalNavigatorKey.currentState!.context)!.connectionFailed, + AppLocalizations.of(globalNavigatorKey.currentState!.context)!.checkYourNetwork, + ); } on ClientException { - globalRef?.read(statusMessageProvider.notifier).state = ('Connection failed', 'Please check your internet connection.'); + if (globalNavigatorKey.currentState?.context == null) return; + globalRef?.read(statusMessageProvider.notifier).state = ( + AppLocalizations.of(globalNavigatorKey.currentState!.context)!.connectionFailed, + AppLocalizations.of(globalNavigatorKey.currentState!.context)!.checkYourNetwork, + ); } finally { ioClient.close(); } From a3294723e093ee065f9f110d76cc7828d45076b8 Mon Sep 17 00:00:00 2001 From: Frank Merkel <138444693+frankmer@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:36:54 +0100 Subject: [PATCH 5/8] buildnumber --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 4c68b43f5..fbaeda193 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ publish_to: none # 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: 4.2.1+402102 # TODO Set the right version number +version: 4.2.1+402103 # TODO Set the right version number # version: major.minor.build + 2x major|2x minor|3x build # version: version number + build number (optional) # android: build-name + versionCode From 76a9c58d48795f4009b113320613cc92e49bb93f Mon Sep 17 00:00:00 2001 From: Frank Merkel <138444693+frankmer@users.noreply.github.com> Date: Thu, 2 Nov 2023 09:50:17 +0100 Subject: [PATCH 6/8] fixed tests --- lib/state_notifiers/token_notifier.dart | 79 +++++++++++-------- lib/widgets/app_wrapper.dart | 2 +- .../state_notifiers/token_notifier_test.dart | 29 ++----- 3 files changed, 56 insertions(+), 54 deletions(-) diff --git a/lib/state_notifiers/token_notifier.dart b/lib/state_notifiers/token_notifier.dart index 824bf7f97..1f6f89696 100644 --- a/lib/state_notifiers/token_notifier.dart +++ b/lib/state_notifiers/token_notifier.dart @@ -35,8 +35,7 @@ import '../utils/view_utils.dart'; import '../widgets/two_step_dialog.dart'; class TokenNotifier extends StateNotifier { - late Future isInitialized; - Future isLoading = Future.value(); + late Future isLoading; final TokenRepository _repo; final QrParser _qrParser; final RsaUtils _rsaUtils; @@ -64,16 +63,19 @@ class TokenNotifier extends StateNotifier { _init(); } - void _init() async { - isInitialized = Future(() async { + Future _init() async { + isLoading = Future(() async { await loadFromRepo(); - return true; + return; }); + await isLoading; } - void _saveOrReplaceTokensRepo(List tokens) async { + Future _saveOrReplaceTokensRepo(List tokens) async { + await isLoading; isLoading = Future(() async { final failedTokens = await _repo.saveOrReplaceTokens(tokens); + Logger.warning('Saved Tokens to repo.!!'); if (failedTokens.isNotEmpty) { Logger.warning( 'Saving tokens failed. Failed Tokens: ${failedTokens.length}', @@ -82,9 +84,11 @@ class TokenNotifier extends StateNotifier { state = state.addOrReplaceTokens(failedTokens); } }); + await isLoading; } - void _deleteTokensRepo(List tokens) async { + Future _deleteTokensRepo(List tokens) async { + await isLoading; isLoading = Future(() async { final failedTokens = await _repo.deleteTokens(tokens); state = state.addOrReplaceTokens(failedTokens); @@ -92,6 +96,7 @@ class TokenNotifier extends StateNotifier { globalRef?.read(settingsProvider.notifier).setHidePushTokens(isHidden: false); } }); + await isLoading; } Future loadFromRepo() async { @@ -129,28 +134,33 @@ class TokenNotifier extends StateNotifier { return state.tokens.firstWhereOrNull((element) => element.id == id); } - void incrementCounter(HOTPToken token) { + Future incrementCounter(HOTPToken token) async { + await isLoading; token = state.currentOf(token)?.copyWith(counter: token.counter + 1) ?? token.copyWith(counter: token.counter + 1); state = state.replaceToken(token); - _saveOrReplaceTokensRepo([token]); + await _saveOrReplaceTokensRepo([token]); } - void removeToken(Token token) { + Future removeToken(Token token) async { + await isLoading; state = state.withoutToken(token); - _deleteTokensRepo([token]); + await _deleteTokensRepo([token]); } - void addOrReplaceToken(Token token) { + Future addOrReplaceToken(Token token) async { + await isLoading; state = state.addOrReplaceToken(token); - _saveOrReplaceTokensRepo([token]); + await _saveOrReplaceTokensRepo([token]); } - void addOrReplaceTokens(List updatedTokens) { + Future addOrReplaceTokens(List updatedTokens) async { + await isLoading; state = state.addOrReplaceTokens(updatedTokens); - _saveOrReplaceTokensRepo(updatedTokens); + await _saveOrReplaceTokensRepo(updatedTokens); } - void updateToken(T token, T Function(T) updater) { + Future updateToken(T token, T Function(T) updater) async { + await isLoading; final current = state.currentOf(token); if (current == null) { Logger.warning('Tried to update a token that does not exist.', name: 'token_notifier.dart#updateToken'); @@ -158,39 +168,43 @@ class TokenNotifier extends StateNotifier { } final updated = updater(current); state = state.replaceToken(updated); - _saveOrReplaceTokensRepo([updated]); + await _saveOrReplaceTokensRepo([updated]); } - void updateTokens(List token, T Function(T) updater) { + Future updateTokens(List token, T Function(T) updater) async { + await isLoading; List updatedTokens = []; for (final t in token) { final current = state.currentOf(t) ?? t; updatedTokens.add(updater(current)); } state = state.replaceTokens(updatedTokens); - _saveOrReplaceTokensRepo(updatedTokens); + await _saveOrReplaceTokensRepo(updatedTokens); } - void handleLink(Uri uri) { + Future handleLink(Uri uri) async { + await isLoading; if (uri.scheme == enumAsString(UriSchemes.otpauth)) { - addTokenFromOtpAuth(otpAuth: uri.toString()); + await addTokenFromOtpAuth(otpAuth: uri.toString()); return; } if (uri.scheme == enumAsString(UriSchemes.pia)) { - addTokenFromPia(pia: uri.toString()); + await addTokenFromPia(pia: uri.toString()); return; } showMessage(message: 'Scheme "${uri.scheme}" is not supported', duration: const Duration(seconds: 3)); } - void addTokenFromPia({required String pia}) async { + Future addTokenFromPia({required String pia}) async { + await isLoading; // TODO: Implement pia:// scheme showMessage(message: 'Scheme "pia" is not implemented yet', duration: const Duration(seconds: 3)); } - void addTokenFromOtpAuth({ + Future addTokenFromOtpAuth({ required String otpAuth, }) async { + await isLoading; Logger.info('Try to handle otpAuth:', name: 'token_notifier.dart#addTokenFromOtpAuth'); try { @@ -219,23 +233,25 @@ class TokenNotifier extends StateNotifier { } on FormatException catch (e) { Logger.warning('Error while parsing otpAuth.', name: 'token_notifier.dart#addTokenFromOtpAuth', error: e); showMessage(message: e.message, duration: const Duration(seconds: 3)); + await isLoading; return; } if (newToken is PushToken && state.tokens.contains(newToken)) { showMessage(message: 'A token with the serial ${newToken.serial} already exists!', duration: const Duration(seconds: 2)); + await isLoading; return; } - addOrReplaceToken(newToken); + await addOrReplaceToken(newToken); if (newToken is PushToken) { - rolloutPushToken(newToken); + await rolloutPushToken(newToken); } - return; } on ArgumentError catch (e, s) { // Error while parsing qr code. Logger.warning('Malformed QR code:', name: 'token_notifier.dart#_handleOtpAuth', error: e, stackTrace: s); showMessage(message: '${e.message}\n Please inform the creator of this qr code about the problem.', duration: const Duration(seconds: 8)); + return; } } @@ -282,17 +298,18 @@ class TokenNotifier extends StateNotifier { return false; } // Save the pending request. - updateToken(token, (p0) => p0.withPushRequest(pr)); + await updateToken(token, (p0) => p0.withPushRequest(pr)); // Remove the request after it expires. int time = pr.expirationDate.difference(DateTime.now()).inMilliseconds; Future.delayed(Duration(milliseconds: time < 1 ? 1 : time), () async => globalRef?.read(tokenProvider.notifier).removePushRequest(pr)); - Logger.info('Added push request ${pr.id} to token ${token.id}', name: 'token_notifier.dart#addPushRequestToToken'); + return true; } - bool removePushRequest(PushRequest pushRequest) { + Future removePushRequest(PushRequest pushRequest) async { + await isLoading; Logger.info('Removing push request ${pushRequest.id}'); PushToken? token = state.tokens.whereType().firstWhereOrNull((t) => t.serial == pushRequest.serial); @@ -300,7 +317,7 @@ class TokenNotifier extends StateNotifier { Logger.warning('The requested token with serial "${pushRequest.serial}" does not exist.', name: 'token_notifier.dart#removePushRequest'); return false; } - updateToken(token, (p0) => p0.withoutPushRequest(pushRequest)); + await updateToken(token, (p0) => p0.withoutPushRequest(pushRequest)); Logger.info('Removed push request from token ${token.id}', name: 'token_notifier.dart#removePushRequest'); return true; diff --git a/lib/widgets/app_wrapper.dart b/lib/widgets/app_wrapper.dart index 1d165bc42..e4230e81e 100644 --- a/lib/widgets/app_wrapper.dart +++ b/lib/widgets/app_wrapper.dart @@ -27,7 +27,7 @@ class ConnectivityListener extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final connectivity = ref.watch(connectivityProvider).asData?.value; final hasConnection = connectivity != null && connectivity != ConnectivityResult.none; - ref.read(tokenProvider.notifier).isInitialized.then( + ref.read(tokenProvider.notifier).isLoading.then( (value) { final hasPushTokens = ref.read(tokenProvider).hasPushTokens; if (!hasConnection && hasPushTokens) { diff --git a/test/unit_test/state_notifiers/token_notifier_test.dart b/test/unit_test/state_notifiers/token_notifier_test.dart index 036e93cee..5445e3045 100644 --- a/test/unit_test/state_notifiers/token_notifier_test.dart +++ b/test/unit_test/state_notifiers/token_notifier_test.dart @@ -105,9 +105,7 @@ void _testTokenNotifier() { ), ); final notifier = container.read(testProvider.notifier); - await notifier.isLoading; - notifier.incrementCounter(before.first); - await notifier.isLoading; + await notifier.incrementCounter(before.first); final state = container.read(testProvider); expect(state, isNotNull); expect(state.tokens, after); @@ -129,9 +127,7 @@ void _testTokenNotifier() { (ref) => TokenNotifier(repository: mockRepo), ); final notifier = container.read(testProvider.notifier); - await notifier.isLoading; - notifier.removeToken(before.last); - await notifier.isLoading; + await notifier.removeToken(before.last); final state = container.read(testProvider); expect(state, isNotNull); expect(state.tokens, after); @@ -156,9 +152,7 @@ void _testTokenNotifier() { ), ); final notifier = container.read(testProvider.notifier); - await notifier.isLoading; - notifier.addOrReplaceToken(after.last); - await notifier.isLoading; + await notifier.addOrReplaceToken(after.last); final state = container.read(testProvider); expect(state, isNotNull); expect(state.tokens, after); @@ -183,9 +177,7 @@ void _testTokenNotifier() { ), ); final notifier = container.read(testProvider.notifier); - await notifier.isLoading; - notifier.addOrReplaceToken(after.last); - await notifier.isLoading; + await notifier.addOrReplaceToken(after.last); final state = container.read(testProvider); expect(state, isNotNull); expect(state.tokens, after); @@ -211,8 +203,7 @@ void _testTokenNotifier() { ), ); final notifier = container.read(testProvider.notifier); - await notifier.isLoading; - notifier.addOrReplaceTokens([...after]); + await notifier.addOrReplaceTokens([...after]); final state = container.read(testProvider); expect(state, isNotNull); expect(state.tokens, after); @@ -243,9 +234,7 @@ void _testTokenNotifier() { (ref) => TokenNotifier(repository: mockRepo, qrParser: mockQrParser), ); final notifier = container.read(testProvider.notifier); - await notifier.isLoading; - notifier.addTokenFromOtpAuth(otpAuth: 'otpAuthString'); - await notifier.isLoading; + await notifier.addTokenFromOtpAuth(otpAuth: 'otpAuthString'); final state = container.read(testProvider); expect(state, isNotNull); after.last = after.last.copyWith(id: state.tokens.last.id); @@ -290,7 +279,6 @@ void _testTokenNotifier() { final notifier = container.read(testProvider.notifier); expect(await notifier.addPushRequestToToken(pr), true); final state = container.read(testProvider); - await notifier.isLoading; expect(state, isNotNull); expect(state.tokens, after); verify(mockRepo.saveOrReplaceTokens([after.first])).called(1); @@ -320,9 +308,7 @@ void _testTokenNotifier() { (ref) => TokenNotifier(repository: mockRepo), ); final notifier = container.read(testProvider.notifier); - await notifier.isLoading; - notifier.removePushRequest(pr); - await notifier.isLoading; + await notifier.removePushRequest(pr); final state = container.read(testProvider); expect(state, isNotNull); expect(state.tokens, after); @@ -364,7 +350,6 @@ void _testTokenNotifier() { ); final notifier = container.read(testProvider.notifier); expect(await notifier.rolloutPushToken(before.first), true); - await notifier.isLoading; final state = container.read(testProvider); expect(state, isNotNull); expect(state.tokens, after); From 9882c4a11edfd8de496b070991aa4016e8deb4ee Mon Sep 17 00:00:00 2001 From: Frank Merkel <138444693+frankmer@users.noreply.github.com> Date: Thu, 2 Nov 2023 11:16:59 +0100 Subject: [PATCH 7/8] renamed status_bar --- lib/views/main_view/main_view.dart | 4 ++-- .../{connectivity_status_bar.dart => status_bar.dart} | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) rename lib/widgets/{connectivity_status_bar.dart => status_bar.dart} (94%) diff --git a/lib/views/main_view/main_view.dart b/lib/views/main_view/main_view.dart index 328fcd00d..82a6df927 100644 --- a/lib/views/main_view/main_view.dart +++ b/lib/views/main_view/main_view.dart @@ -6,7 +6,7 @@ import '../../model/states/app_state.dart'; import '../../utils/logger.dart'; import '../../utils/riverpod_providers.dart'; import '../../widgets/app_wrapper.dart'; -import '../../widgets/connectivity_status_bar.dart'; +import '../../widgets/status_bar.dart'; import 'main_view_widgets/main_view_navigation_bar.dart'; import 'main_view_widgets/main_view_tokens_list.dart'; @@ -56,7 +56,7 @@ class _MainViewState extends ConsumerState with LifecycleMixin { ), ), body: ConnectivityListener( - child: ConnectivityStatusBar( + child: StatusBar( child: Stack( clipBehavior: Clip.antiAliasWithSaveLayer, children: [ diff --git a/lib/widgets/connectivity_status_bar.dart b/lib/widgets/status_bar.dart similarity index 94% rename from lib/widgets/connectivity_status_bar.dart rename to lib/widgets/status_bar.dart index 0caf18bcb..6ee8bfe7f 100644 --- a/lib/widgets/connectivity_status_bar.dart +++ b/lib/widgets/status_bar.dart @@ -6,15 +6,15 @@ import 'package:privacyidea_authenticator/utils/text_size.dart'; import '../utils/riverpod_providers.dart'; -class ConnectivityStatusBar extends ConsumerStatefulWidget { +class StatusBar extends ConsumerStatefulWidget { final Widget child; - const ConnectivityStatusBar({super.key, required this.child}); + const StatusBar({super.key, required this.child}); @override - ConsumerState createState() => _ConnectivityStatusBarState(); + ConsumerState createState() => _ConnectivityStatusBarState(); } -class _ConnectivityStatusBarState extends ConsumerState { +class _ConnectivityStatusBarState extends ConsumerState { (String, String?)? previousStatusMessage; (String, String?)? currentStatusMessage; Queue<(String, String?)> statusbarQueue = Queue(); @@ -27,10 +27,7 @@ class _ConnectivityStatusBarState extends ConsumerState { @override Widget build(BuildContext context) { final newStatusMessage = ref.watch(statusMessageProvider); - // if (newStatusMessage != previousStatusMessage && newStatusMessage != currentStatusMessage) { - // previousStatusMessage = newStatusMessage; _addToQueueIfNotInQueue(newStatusMessage); - // } return widget.child; } From f837730c6af942394a6d0a057eb7bf22edbb8f67 Mon Sep 17 00:00:00 2001 From: Frank Merkel <138444693+frankmer@users.noreply.github.com> Date: Thu, 2 Nov 2023 11:28:06 +0100 Subject: [PATCH 8/8] resolved conversations --- lib/main_netknights.dart | 2 +- lib/state_notifiers/token_notifier.dart | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/main_netknights.dart b/lib/main_netknights.dart index 6fb98ec99..05f2621c0 100644 --- a/lib/main_netknights.dart +++ b/lib/main_netknights.dart @@ -57,7 +57,7 @@ class PrivacyIDEAAuthenticator extends ConsumerWidget { globalRef = ref; final locale = ref.watch(settingsProvider).currentLocale; return MaterialApp( - debugShowCheckedModeBanner: false, + debugShowCheckedModeBanner: true, navigatorKey: globalNavigatorKey, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, diff --git a/lib/state_notifiers/token_notifier.dart b/lib/state_notifiers/token_notifier.dart index 1f6f89696..2317f4eef 100644 --- a/lib/state_notifiers/token_notifier.dart +++ b/lib/state_notifiers/token_notifier.dart @@ -75,7 +75,6 @@ class TokenNotifier extends StateNotifier { await isLoading; isLoading = Future(() async { final failedTokens = await _repo.saveOrReplaceTokens(tokens); - Logger.warning('Saved Tokens to repo.!!'); if (failedTokens.isNotEmpty) { Logger.warning( 'Saving tokens failed. Failed Tokens: ${failedTokens.length}',