From 8b7bbae00bea229cdf37c7431a5d9f2d82310d9b Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Wed, 14 Jun 2023 11:53:37 +0200 Subject: [PATCH] feat: Add debug output, run app in guarded zone --- .../dio_http_error_interceptor.dart | 15 +- lib/core/security/session_manager.dart | 4 +- lib/features/home/view/home_page.dart | 1 + lib/features/home/view/home_route.dart | 19 +- .../login/cubit/authentication_cubit.dart | 196 ++++++++++++++++-- lib/features/login/view/login_page.dart | 8 +- .../cubit/document_paging_bloc_mixin.dart | 12 +- lib/main.dart | 170 ++++++++------- .../authentication_api_impl.dart | 1 + 9 files changed, 315 insertions(+), 111 deletions(-) diff --git a/lib/core/interceptor/dio_http_error_interceptor.dart b/lib/core/interceptor/dio_http_error_interceptor.dart index cf65551..4f3ee47 100644 --- a/lib/core/interceptor/dio_http_error_interceptor.dart +++ b/lib/core/interceptor/dio_http_error_interceptor.dart @@ -19,11 +19,15 @@ class DioHttpErrorInterceptor extends Interceptor { } else if (err.response?.statusCode == 403) { var data = err.response!.data; if (data is Map && data.containsKey("detail")) { - handler.reject(DioError( - requestOptions: err.requestOptions, - error: ServerMessageException(data['detail']), - response: err.response, - )); + handler.reject( + DioError( + message: data['detail'], + requestOptions: err.requestOptions, + error: ServerMessageException(data['detail']), + response: err.response, + type: DioErrorType.unknown, + ), + ); return; } } else if (err.error is SocketException) { @@ -31,6 +35,7 @@ class DioHttpErrorInterceptor extends Interceptor { if (ex.osError?.errorCode == _OsErrorCodes.serverUnreachable.code) { return handler.reject( DioError( + message: "The server could not be reached. Is the device offline?", error: const PaperlessServerException(ErrorCode.deviceOffline), requestOptions: err.requestOptions, type: DioErrorType.connectionTimeout, diff --git a/lib/core/security/session_manager.dart b/lib/core/security/session_manager.dart index 9ff8219..3a366e1 100644 --- a/lib/core/security/session_manager.dart +++ b/lib/core/security/session_manager.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; import 'package:flutter/material.dart'; +import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart'; import 'package:paperless_mobile/core/interceptor/retry_on_connection_change_interceptor.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; @@ -22,13 +23,14 @@ class SessionManager extends ValueNotifier { BaseOptions(contentType: Headers.jsonContentType), ); dio.options - ..receiveTimeout = const Duration(seconds: 20) + ..receiveTimeout = const Duration(seconds: 30) ..sendTimeout = const Duration(seconds: 60) ..responseType = ResponseType.json; (dio.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate = (client) => client..badCertificateCallback = (cert, host, port) => true; dio.interceptors.addAll([ ...interceptors, + DioHttpErrorInterceptor(), PrettyDioLogger( compact: true, responseBody: false, diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index f34ab53..c0fe563 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -12,6 +12,7 @@ import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; +import 'package:paperless_mobile/core/exception/server_message_exception.dart'; import 'package:paperless_mobile/core/global/constants.dart'; import 'package:paperless_mobile/core/navigation/push_routes.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; diff --git a/lib/features/home/view/home_route.dart b/lib/features/home/view/home_route.dart index bea308c..f27d48c 100644 --- a/lib/features/home/view/home_route.dart +++ b/lib/features/home/view/home_route.dart @@ -130,10 +130,17 @@ class HomeRoute extends StatelessWidget { .get(currentLocalUserId)!, )..initialize(), ), - Provider(create: (context) => DocumentScannerCubit(context.read())), - ProxyProvider4( - update: (context, docApi, statsApi, labelRepo, notifier, previous) => + Provider( + create: (context) => + DocumentScannerCubit(context.read())), + ProxyProvider4< + PaperlessDocumentsApi, + PaperlessServerStatsApi, + LabelRepository, + DocumentChangedNotifier, + InboxCubit>( + update: (context, docApi, statsApi, labelRepo, notifier, + previous) => InboxCubit( docApi, statsApi, @@ -143,9 +150,7 @@ class HomeRoute extends StatelessWidget { ), ProxyProvider( update: (context, savedViewRepo, previous) => - SavedViewCubit( - savedViewRepo, - ), + SavedViewCubit(savedViewRepo), ), ProxyProvider( update: (context, value, previous) => LabelCubit(value), diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index 1a221fb..63c3b19 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -3,6 +3,7 @@ import 'package:flutter/widgets.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; +import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_extensions.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart'; @@ -37,7 +38,10 @@ class AuthenticationCubit extends Cubit { }) async { assert(credentials.username != null && credentials.password != null); final localUserId = "${credentials.username}@$serverUrl"; - + _debugPrintMessage( + "login", + "Trying to login $localUserId...", + ); await _addUser( localUserId, serverUrl, @@ -60,6 +64,10 @@ class AuthenticationCubit extends Cubit { localUserId: localUserId, ), ); + _debugPrintMessage( + "login", + "User successfully logged in.", + ); } /// Switches to another account if it exists. @@ -161,26 +169,57 @@ class AuthenticationCubit extends Cubit { /// Performs a conditional hydration based on the local authentication success. /// Future restoreSessionState() async { + _debugPrintMessage( + "restoreSessionState", + "Trying to restore previous session...", + ); final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; final localUserId = globalSettings.currentLoggedInUser; if (localUserId == null) { + _debugPrintMessage( + "restoreSessionState", + "There is nothing to restore.", + ); // If there is nothing to restore, we can quit here. return; } final localUserAccountBox = Hive.box(HiveBoxes.localUserAccount); final localUserAccount = localUserAccountBox.get(localUserId)!; - + _debugPrintMessage( + "restoreSessionState", + "Checking if biometric authentication is required...", + ); if (localUserAccount.settings.isBiometricAuthenticationEnabled) { + _debugPrintMessage( + "restoreSessionState", + "Biometric authentication required, waiting for user to authenticate...", + ); final localAuthSuccess = await _localAuthService .authenticateLocalUser("Authenticate to log back in"); //TODO: INTL if (!localAuthSuccess) { emit(const AuthenticationState.requriresLocalAuthentication()); + _debugPrintMessage( + "restoreSessionState", + "User could not be authenticated.", + ); return; } + _debugPrintMessage( + "restoreSessionState", + "User successfully autheticated.", + ); + } else { + _debugPrintMessage( + "restoreSessionState", + "Biometric authentication not configured, skipping.", + ); } - + _debugPrintMessage( + "restoreSessionState", + "Trying to retrieve authentication credentials...", + ); final authentication = await withEncryptedBox( HiveBoxes.localUserCredentials, (box) { @@ -188,14 +227,32 @@ class AuthenticationCubit extends Cubit { }); if (authentication == null) { + _debugPrintMessage( + "restoreSessionState", + "Could not retrieve existing authentication credentials.", + ); throw Exception( - "User should be authenticated but no authentication information was found."); //TODO: INTL + "User should be authenticated but no authentication information was found.", + ); //TODO: INTL } + _debugPrintMessage( + "restoreSessionState", + "Authentication credentials successfully retrieved.", + ); + _debugPrintMessage( + "restoreSessionState", + "Updating current session state...", + ); _sessionManager.updateSettings( clientCertificate: authentication.clientCertificate, authToken: authentication.token, baseUrl: localUserAccount.serverUrl, ); + _debugPrintMessage( + "restoreSessionState", + "Current session state successfully updated.", + ); + final apiVersion = await _getApiVersion(_sessionManager.client); await _updateRemoteUser( _sessionManager, @@ -208,20 +265,41 @@ class AuthenticationCubit extends Cubit { localUserId: localUserId, ), ); + _debugPrintMessage( + "restoreSessionState", + "Session was successfully restored.", + ); } Future logout() async { + _debugPrintMessage( + "logout", + "Trying to log out current user...", + ); await _resetExternalState(); final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; globalSettings.currentLoggedInUser = null; await globalSettings.save(); + emit(const AuthenticationState.unauthenticated()); + _debugPrintMessage( + "logout", + "User successfully logged out.", + ); } Future _resetExternalState() async { + _debugPrintMessage( + "_resetExternalState", + "Resetting session manager and clearing storage...", + ); _sessionManager.resetSettings(); await HydratedBloc.storage.clear(); + _debugPrintMessage( + "_resetExternalState", + "Session manager successfully reset and storage cleared.", + ); } Future _addUser( @@ -232,6 +310,7 @@ class AuthenticationCubit extends Cubit { SessionManager sessionManager, ) async { assert(credentials.username != null && credentials.password != null); + _debugPrintMessage("_addUser", "Adding new user $localUserId..."); sessionManager.updateSettings( baseUrl: serverUrl, @@ -240,11 +319,21 @@ class AuthenticationCubit extends Cubit { final authApi = _apiFactory.createAuthenticationApi(sessionManager.client); + _debugPrintMessage( + "_addUser", + "Trying to login user ${credentials.username} on $serverUrl...", + ); + final token = await authApi.login( username: credentials.username!, password: credentials.password!, ); + _debugPrintMessage( + "_addUser", + "Successfully acquired token.", + ); + sessionManager.updateSettings( baseUrl: serverUrl, clientCertificate: clientCert, @@ -257,17 +346,41 @@ class AuthenticationCubit extends Cubit { Hive.box(HiveBoxes.localUserAppState); if (userAccountBox.containsKey(localUserId)) { + _debugPrintMessage( + "_addUser", + "An error occurred! The user $localUserId already exists.", + ); throw Exception("User already exists!"); } final apiVersion = await _getApiVersion(sessionManager.client); - - final serverUser = await _apiFactory - .createUserApi( - sessionManager.client, - apiVersion: apiVersion, - ) - .findCurrentUser(); - + _debugPrintMessage( + "_addUser", + "Trying to fetch user object for $localUserId...", + ); + late UserModel serverUser; + try { + serverUser = await _apiFactory + .createUserApi( + sessionManager.client, + apiVersion: apiVersion, + ) + .findCurrentUser(); + } on DioError catch (error, stackTrace) { + _debugPrintMessage( + "_addUser", + "An error occurred: ${error.message}", + stackTrace: stackTrace, + ); + rethrow; + } + _debugPrintMessage( + "_addUser", + "User object successfully fetched.", + ); + _debugPrintMessage( + "_addUser", + "Persisting local user account...", + ); // Create user account await userAccountBox.put( localUserId, @@ -278,15 +391,29 @@ class AuthenticationCubit extends Cubit { paperlessUser: serverUser, ), ); - + _debugPrintMessage( + "_addUser", + "Local user account successfully persisted.", + ); + _debugPrintMessage( + "_addUser", + "Persisting user state...", + ); // Create user state await userStateBox.put( localUserId, LocalUserAppState(userId: localUserId), ); - + _debugPrintMessage( + "_addUser", + "User state successfully persisted.", + ); // Save credentials in encrypted box await withEncryptedBox(HiveBoxes.localUserCredentials, (box) async { + _debugPrintMessage( + "_addUser", + "Saving user credentials inside encrypted storage...", + ); await box.put( localUserId, UserCredentials( @@ -294,17 +421,32 @@ class AuthenticationCubit extends Cubit { clientCertificate: clientCert, ), ); + _debugPrintMessage( + "_addUser", + "User credentials successfully saved.", + ); }); final hostsBox = Hive.box(HiveBoxes.hosts); if (!hostsBox.values.contains(serverUrl)) { await hostsBox.add(serverUrl); } + return serverUser.id; } Future _getApiVersion(Dio dio) async { + _debugPrintMessage( + "_getApiVersion", + "Trying to fetch API version...", + ); final response = await dio.get("/api/"); - return int.parse(response.headers.value('x-api-version') ?? "3"); + final apiVersion = + int.parse(response.headers.value('x-api-version') ?? "3"); + _debugPrintMessage( + "_getApiVersion", + "API version ($apiVersion) successfully retrieved.", + ); + return apiVersion; } /// Fetches possibly updated (permissions, name, updated server version and thus new user model, ...) remote user data. @@ -313,13 +455,37 @@ class AuthenticationCubit extends Cubit { LocalUserAccount localUserAccount, int apiVersion, ) async { + _debugPrintMessage( + "_updateRemoteUser", + "Updating paperless user object...", + ); final updatedPaperlessUser = await _apiFactory .createUserApi( sessionManager.client, apiVersion: apiVersion, ) .findCurrentUser(); + localUserAccount.paperlessUser = updatedPaperlessUser; await localUserAccount.save(); + _debugPrintMessage( + "_updateRemoteUser", + "Paperless user object successfully updated.", + ); + } + + void _debugPrintMessage( + String methodName, + String message, { + Object? error, + StackTrace? stackTrace, + }) { + debugPrint("AuthenticationCubit#$methodName: $message"); + if (error != null) { + debugPrint(error.toString()); + } + if (stackTrace != null) { + debugPrintStack(stackTrace: stackTrace); + } } } diff --git a/lib/features/login/view/login_page.dart b/lib/features/login/view/login_page.dart index d5415a2..969ab34 100644 --- a/lib/features/login/view/login_page.dart +++ b/lib/features/login/view/login_page.dart @@ -4,8 +4,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:hive_flutter/adapters.dart'; +import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; +import 'package:paperless_mobile/core/exception/server_message_exception.dart'; import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/client_certificate_form_model.dart'; @@ -147,7 +149,11 @@ class _LoginPageState extends State { form[ServerAddressFormField.fkServerAddress], clientCert, ); - } on Exception catch (error) { + } on PaperlessServerException catch (error) { + showErrorMessage(context, error); + } on ServerMessageException catch (error) { + showLocalizedError(context, error.message); + } catch (error) { showGenericError(context, error); } } diff --git a/lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart b/lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart index e2f96d4..4d7ed16 100644 --- a/lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart +++ b/lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart @@ -24,11 +24,13 @@ mixin DocumentPagingBlocMixin final newFilter = state.filter.copyWith(page: state.filter.page + 1); try { final result = await api.findAll(newFilter); - emit(state.copyWithPaged( - hasLoaded: true, - filter: newFilter, - value: [...state.value, result], - )); + emit( + state.copyWithPaged( + hasLoaded: true, + filter: newFilter, + value: [...state.value, result], + ), + ); } finally { await onFilterUpdated(newFilter); emit(state.copyWithPaged(isLoading: false)); diff --git a/lib/main.dart b/lib/main.dart index 32cc0f5..81e395d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,7 @@ import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/intl_standalone.dart'; import 'package:local_auth/local_auth.dart'; +import 'package:mock_server/mock_server.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/constants.dart'; @@ -22,6 +23,7 @@ import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart'; +import 'package:paperless_mobile/core/exception/server_message_exception.dart'; import 'package:paperless_mobile/core/factory/paperless_api_factory.dart'; import 'package:paperless_mobile/core/factory/paperless_api_factory_impl.dart'; import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart'; @@ -41,14 +43,11 @@ import 'package:paperless_mobile/features/login/view/login_page.dart'; import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; import 'package:paperless_mobile/features/settings/view/pages/switching_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; -import 'package:paperless_mobile/features/sharing/share_intent_queue.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; import 'package:paperless_mobile/theme.dart'; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; -import 'package:receive_sharing_intent/receive_sharing_intent.dart'; -import 'package:mock_server/mock_server.dart'; String get defaultPreferredLocaleSubtag { String preferredLocale = Platform.localeName.split("_").first; @@ -77,95 +76,112 @@ Future _initHive() async { } void main() async { - Paint.enableDithering = true; - if (kDebugMode) { - // URL: http://localhost:3131 - // Login: admin:test - await LocalMockApiServer( - // RandomDelayGenerator( - // const Duration(milliseconds: 100), - // const Duration(milliseconds: 800), - // ), - ) - .start(); - } - await _initHive(); - final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); - final globalSettingsBox = Hive.box(HiveBoxes.globalSettings); - final globalSettings = globalSettingsBox.getValue()!; + runZonedGuarded(() async { + Paint.enableDithering = true; + if (kDebugMode) { + // URL: http://localhost:3131 + // Login: admin:test + await LocalMockApiServer( + // RandomDelayGenerator( + // const Duration(milliseconds: 100), + // const Duration(milliseconds: 800), + // ), + ) + .start(); + } + await _initHive(); + final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); + final globalSettingsBox = + Hive.box(HiveBoxes.globalSettings); + final globalSettings = globalSettingsBox.getValue()!; - await findSystemLocale(); - packageInfo = await PackageInfo.fromPlatform(); - if (Platform.isAndroid) { - androidInfo = await DeviceInfoPlugin().androidInfo; - } - if (Platform.isIOS) { - iosInfo = await DeviceInfoPlugin().iosInfo; - } + await findSystemLocale(); + packageInfo = await PackageInfo.fromPlatform(); + if (Platform.isAndroid) { + androidInfo = await DeviceInfoPlugin().androidInfo; + } + if (Platform.isIOS) { + iosInfo = await DeviceInfoPlugin().iosInfo; + } - final connectivity = Connectivity(); - final localAuthentication = LocalAuthentication(); - final connectivityStatusService = ConnectivityStatusServiceImpl(connectivity); - final localAuthService = LocalAuthenticationService(localAuthentication); + final connectivity = Connectivity(); + final localAuthentication = LocalAuthentication(); + final connectivityStatusService = + ConnectivityStatusServiceImpl(connectivity); + final localAuthService = LocalAuthenticationService(localAuthentication); - HydratedBloc.storage = await HydratedStorage.build( - storageDirectory: await getApplicationDocumentsDirectory(), - ); + HydratedBloc.storage = await HydratedStorage.build( + storageDirectory: await getApplicationDocumentsDirectory(), + ); - FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); - final languageHeaderInterceptor = LanguageHeaderInterceptor( - globalSettings.preferredLocaleSubtag, - ); - // Manages security context, required for self signed client certificates - final sessionManager = SessionManager([ - DioHttpErrorInterceptor(), - languageHeaderInterceptor, - ]); + final languageHeaderInterceptor = LanguageHeaderInterceptor( + globalSettings.preferredLocaleSubtag, + ); + // Manages security context, required for self signed client certificates + final sessionManager = SessionManager([ + languageHeaderInterceptor, + ]); - // Initialize Blocs/Cubits - final connectivityCubit = ConnectivityCubit(connectivityStatusService); + // Initialize Blocs/Cubits + final connectivityCubit = ConnectivityCubit(connectivityStatusService); - // Load application settings and stored authentication data - await connectivityCubit.initialize(); + // Load application settings and stored authentication data + await connectivityCubit.initialize(); - final localNotificationService = LocalNotificationService(); - await localNotificationService.initialize(); + final localNotificationService = LocalNotificationService(); + await localNotificationService.initialize(); - //Update language header in interceptor on language change. - globalSettingsBox.listenable().addListener(() { - languageHeaderInterceptor.preferredLocaleSubtag = - globalSettings.preferredLocaleSubtag; - }); + //Update language header in interceptor on language change. + globalSettingsBox.listenable().addListener(() { + languageHeaderInterceptor.preferredLocaleSubtag = + globalSettings.preferredLocaleSubtag; + }); - final apiFactory = PaperlessApiFactoryImpl(sessionManager); + final apiFactory = PaperlessApiFactoryImpl(sessionManager); - runApp( - MultiProvider( - providers: [ - ChangeNotifierProvider.value(value: sessionManager), - Provider.value(value: localAuthService), - Provider.value(value: connectivity), - Provider.value( - value: connectivityStatusService), - Provider.value( - value: localNotificationService), - Provider.value(value: DocumentChangedNotifier()), - ], - child: MultiBlocProvider( + runApp( + MultiProvider( providers: [ - BlocProvider.value(value: connectivityCubit), - BlocProvider( - create: (context) => AuthenticationCubit( - localAuthService, apiFactory, sessionManager), - ) + ChangeNotifierProvider.value(value: sessionManager), + Provider.value(value: localAuthService), + Provider.value(value: connectivity), + Provider.value( + value: connectivityStatusService), + Provider.value( + value: localNotificationService), + Provider.value(value: DocumentChangedNotifier()), ], - child: PaperlessMobileEntrypoint( - paperlessProviderFactory: apiFactory, + child: MultiBlocProvider( + providers: [ + BlocProvider.value(value: connectivityCubit), + BlocProvider( + create: (context) => AuthenticationCubit( + localAuthService, apiFactory, sessionManager), + ), + ], + child: PaperlessMobileEntrypoint( + paperlessProviderFactory: apiFactory, + ), ), ), - ), - ); + ); + }, (error, stack) { + String message = switch (error) { + PaperlessServerException e => e.details ?? error.toString(), + ServerMessageException e => e.message, + _ => error.toString() + }; + debugPrint("An unepxected exception has occured!"); + debugPrint(message); + debugPrintStack(stackTrace: stack); + // if (_rootScaffoldKey.currentContext != null) { + // ScaffoldMessenger.maybeOf(_rootScaffoldKey.currentContext!) + // ?..hideCurrentSnackBar() + // ..showSnackBar(SnackBar(content: Text(message))); + // } + }); } class PaperlessMobileEntrypoint extends StatefulWidget { diff --git a/packages/paperless_api/lib/src/modules/authentication_api/authentication_api_impl.dart b/packages/paperless_api/lib/src/modules/authentication_api/authentication_api_impl.dart index d2b313d..611b261 100644 --- a/packages/paperless_api/lib/src/modules/authentication_api/authentication_api_impl.dart +++ b/packages/paperless_api/lib/src/modules/authentication_api/authentication_api_impl.dart @@ -1,4 +1,5 @@ import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; import 'package:paperless_api/src/models/paperless_server_exception.dart'; import 'package:paperless_api/src/modules/authentication_api/authentication_api.dart';