From eeabcb502ee4f6f54b4c2a198c69f4b631eb5334 Mon Sep 17 00:00:00 2001 From: Ayush Chaudhary <95746190+Ayush0Chaudhary@users.noreply.github.com> Date: Sat, 28 Jan 2023 03:24:46 +0530 Subject: [PATCH] Fixes #1378 Update Talawa to give a meaningful message if the URL isn't available. (#1380) * feat: Add proper error handling if url not available. * Fix #1378 Update Talawa to give a meaningful message if the URL isn't available. * fixes the failed checks. * Make the suggested changes * Add error widget * Add custom error snackbar for Talawa * Make the notif bar scrollable. * Remove the useless part * Add dialog box with scroll instead of snackbar * added the custom widget * added the custom widget * Completed the request changes. * removed all the warnings * changed the app-setting test * Add test for custom error widget and did err-handling in qrScan * removed all warnings * Update build.gradle * Delete google-services.json * Update build.gradle * Delete GoogleService-Info.plist * Delete firebase_app_id_file.json --- lib/services/database_mutation_functions.dart | 7 +- lib/services/navigation_service.dart | 28 ++++++ .../set_url_view_model.dart | 21 ++++- lib/widgets/organization_list.dart | 7 +- lib/widgets/talawa_error_dialog.dart | 35 ++++++++ lib/widgets/talawa_error_widget.dart | 40 +++++++++ pubspec.lock | 7 ++ pubspec.yaml | 1 + .../set_url_view_model_test.dart | 86 ++++++++++++++++++- .../app_settings/app_setting_page_test.dart | 21 ++--- .../widgets/custom_alert_dialog_test.dart | 1 - .../widgets/talawa_error_dialog_test.dart | 72 ++++++++++++++++ .../widgets/talawa_error_widget_test.dart | 58 +++++++++++++ 13 files changed, 360 insertions(+), 24 deletions(-) create mode 100644 lib/widgets/talawa_error_dialog.dart create mode 100644 lib/widgets/talawa_error_widget.dart create mode 100644 test/widget_tests/widgets/talawa_error_dialog_test.dart create mode 100644 test/widget_tests/widgets/talawa_error_widget_test.dart diff --git a/lib/services/database_mutation_functions.dart b/lib/services/database_mutation_functions.dart index 08bf252c3..655870bd9 100644 --- a/lib/services/database_mutation_functions.dart +++ b/lib/services/database_mutation_functions.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:talawa/locator.dart'; @@ -37,7 +39,10 @@ class DataBaseMutationFunctions { if (exception.linkException != null) { debugPrint(exception.linkException.toString()); if (showSnackBar) { - navigationService.showSnackBar("Server not running/wrong url"); + Timer(const Duration(seconds: 2), () { + navigationService + .showTalawaErrorDialog("Server not running/wrong url"); + }); } return false; } diff --git a/lib/services/navigation_service.dart b/lib/services/navigation_service.dart index 825d7f230..f918f499c 100644 --- a/lib/services/navigation_service.dart +++ b/lib/services/navigation_service.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:talawa/widgets/talawa_error_dialog.dart'; +import 'package:talawa/widgets/talawa_error_widget.dart'; + class NavigationService { GlobalKey navigatorKey = GlobalKey(); @@ -62,6 +65,31 @@ class NavigationService { ); } + void showTalawaErrorWidget( + String errorMessage, { + Duration duration = const Duration(seconds: 2), + }) { + ScaffoldMessenger.of(navigatorKey.currentContext!).showSnackBar(SnackBar( + padding: EdgeInsets.zero, + content: TalawaErrorWidget( + errorMessage: errorMessage, + ), + // backgroundColor: Colors.grey, + backgroundColor: const Color.fromRGBO(65, 65, 66, 1), + )); + } + + void showTalawaErrorDialog(String errorMessage) { + showDialog( + context: navigatorKey.currentContext!, + barrierColor: Colors.transparent, + barrierDismissible: false, + builder: (BuildContext context) { + return TalawaErrorDialog(errorMessage); + }, + ); + } + void pop() { return navigatorKey.currentState!.pop(); } diff --git a/lib/view_model/pre_auth_view_models/set_url_view_model.dart b/lib/view_model/pre_auth_view_models/set_url_view_model.dart index 22915318d..53fb62b00 100644 --- a/lib/view_model/pre_auth_view_models/set_url_view_model.dart +++ b/lib/view_model/pre_auth_view_models/set_url_view_model.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; +import 'package:qr_flutter/qr_flutter.dart'; import 'package:talawa/locator.dart'; import 'package:talawa/services/size_config.dart'; import 'package:talawa/utils/validators.dart'; @@ -87,8 +88,10 @@ class SetUrlViewModel extends BaseModel { graphqlConfig.getOrgUrl(); navigationService.pushScreen(navigateTo, arguments: argument); } else { - navigationService - .showSnackBar("URL doesn't exist/no connection please check"); + // navigationService + // .showSnackBar("URL doesn't exist/no connection please check"); + navigationService.showTalawaErrorWidget( + "URL doesn't exist/no connection please check"); } } } @@ -165,9 +168,19 @@ class SetUrlViewModel extends BaseModel { graphqlConfig.getOrgUrl(); Navigator.pop(navigationService.navigatorKey.currentContext!); navigationService.pushScreen('/selectOrg', arguments: orgId); + } on CameraException catch (e) { + debugPrint(e.toString()); + navigationService.showTalawaErrorWidget("The Camera is not working"); + } on QrEmbeddedImageException catch (e) { + debugPrint(e.toString()); + navigationService.showTalawaErrorDialog("The QR is not Working"); + } on QrUnsupportedVersionException catch (e) { + debugPrint(e.toString()); + navigationService + .showTalawaErrorDialog("This QR version is not Supported."); } on Exception catch (e) { - print(e); - print('invalid app qr'); + debugPrint(e.toString()); + navigationService.showTalawaErrorWidget("This QR is not for the App"); } } }); diff --git a/lib/widgets/organization_list.dart b/lib/widgets/organization_list.dart index f838ed647..05c1fb811 100644 --- a/lib/widgets/organization_list.dart +++ b/lib/widgets/organization_list.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; @@ -16,6 +18,7 @@ class OrganizationList extends StatelessWidget { @override Widget build(BuildContext context) { model.organizations = []; + int noOfRefetch = 0; return GraphQLProvider( client: ValueNotifier(graphqlConfig.clientToQuery()), child: Query( @@ -34,13 +37,15 @@ class OrganizationList extends StatelessWidget { if (result.hasException) { final isException = databaseFunctions.encounteredExceptionOrError( result.exception!, - showSnackBar: false, + showSnackBar: noOfRefetch == 0, ); if (isException != null) { if (isException) { refetch!(); + noOfRefetch++; } else { refetch!(); + noOfRefetch++; } } } else { diff --git a/lib/widgets/talawa_error_dialog.dart b/lib/widgets/talawa_error_dialog.dart new file mode 100644 index 000000000..10b64166d --- /dev/null +++ b/lib/widgets/talawa_error_dialog.dart @@ -0,0 +1,35 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; + +class TalawaErrorDialog extends StatelessWidget { + const TalawaErrorDialog(this.errorMessage, {Key? key}) : super(key: key); + final String errorMessage; + @override + Widget build(BuildContext context) { + return SizedBox( + child: AlertDialog( + title: const Text( + "Error", + style: TextStyle(color: Colors.red), + ), + content: SizedBox( + width: 200, + height: 75, + child: AutoSizeText( + errorMessage, + style: const TextStyle(fontSize: 20), + maxLines: 3, + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ), + ); + } +} diff --git a/lib/widgets/talawa_error_widget.dart b/lib/widgets/talawa_error_widget.dart new file mode 100644 index 000000000..19ffd98e0 --- /dev/null +++ b/lib/widgets/talawa_error_widget.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +class TalawaErrorWidget extends StatelessWidget { + const TalawaErrorWidget({Key? key, required this.errorMessage}) + : super(key: key); + final String errorMessage; + @override + Widget build(BuildContext context) { + return Row( + children: [ + Container( + width: 20, + height: 80, + decoration: const BoxDecoration(color: Colors.red), + ), + const SizedBox( + width: 10, + ), + const Icon( + Icons.error, + color: Colors.red, + size: 35, + ), + const SizedBox( + width: 10, + ), + Expanded( + flex: 1, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Text( + errorMessage, + style: const TextStyle(color: Colors.white), + ), + ), + ) + ], + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 812d07971..322b70fc8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -29,6 +29,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.9.0" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" boolean_selector: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 901bc18d7..c5f5cec9d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ environment: sdk: ">=2.12.0 <3.0.0" dependencies: + auto_size_text: ^3.0.0 cached_network_image: ^3.1.0 connectivity_plus: ^2.3.1 cupertino_icons: ^1.0.3 diff --git a/test/view_model_tests/pre_auth_view_models/set_url_view_model_test.dart b/test/view_model_tests/pre_auth_view_models/set_url_view_model_test.dart index 433447be7..cfe738176 100644 --- a/test/view_model_tests/pre_auth_view_models/set_url_view_model_test.dart +++ b/test/view_model_tests/pre_auth_view_models/set_url_view_model_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:hive/hive.dart'; import 'package:mockito/mockito.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; +import 'package:qr_flutter/qr_flutter.dart'; import 'package:talawa/locator.dart'; import 'package:talawa/services/size_config.dart'; import 'package:talawa/utils/validators.dart'; @@ -89,8 +90,8 @@ Future main() async { await model.checkURLandNavigate('/', 'arguments'); - verify(navigationService - .showSnackBar("URL doesn't exist/no connection please check")); + verify(navigationService.showTalawaErrorWidget( + "URL doesn't exist/no connection please check")); }); testWidgets('Check if scanQR() is working fine', (tester) async { @@ -123,7 +124,82 @@ Future main() async { .onQRViewCreated(controller); }); - testWidgets('Check if _onQRViewCreated() is working fine when throws', + testWidgets( + 'Check if _onQRViewCreated() is working fine when throws CameraException', + (tester) async { + await tester.pumpWidget( + MaterialApp( + home: TestWidget(model), + navigatorKey: navigationService.navigatorKey, + ), + ); + + final controller = MockQRViewController(); + when(controller.scannedDataStream).thenAnswer((_) async* { + yield Barcode('qr?orgId=1&scan', BarcodeFormat.qrcode, null); + }); + // when(controller.stopCamera()) + // .thenThrow(Exception({"errorType": "error"})); + + when(controller.stopCamera()) + .thenThrow(CameraException("200", "cameraException")); + await tester.tap(find.byType(FloatingActionButton)); + await tester.pump(); + + (tester.widget(find.byType(QRView)) as QRView) + .onQRViewCreated(controller); + }); + testWidgets( + 'Check if _onQRViewCreated() is working fine when throws QrEmbeddedImageException', + (tester) async { + await tester.pumpWidget( + MaterialApp( + home: TestWidget(model), + navigatorKey: navigationService.navigatorKey, + ), + ); + + final controller = MockQRViewController(); + when(controller.scannedDataStream).thenAnswer((_) async* { + yield Barcode('qr?orgId=1&scan', BarcodeFormat.qrcode, null); + }); + // when(controller.stopCamera()) + // .thenThrow(Exception({"errorType": "error"})); + + when(controller.stopCamera()) + .thenThrow(QrEmbeddedImageException("error")); + await tester.tap(find.byType(FloatingActionButton)); + await tester.pump(); + + (tester.widget(find.byType(QRView)) as QRView) + .onQRViewCreated(controller); + }); + testWidgets( + 'Check if _onQRViewCreated() is working fine when throws QrUnsupportedVersionException', + (tester) async { + await tester.pumpWidget( + MaterialApp( + home: TestWidget(model), + navigatorKey: navigationService.navigatorKey, + ), + ); + + final controller = MockQRViewController(); + when(controller.scannedDataStream).thenAnswer((_) async* { + yield Barcode('qr?orgId=1&scan', BarcodeFormat.qrcode, null); + }); + // when(controller.stopCamera()) + // .thenThrow(Exception({"errorType": "error"})); + + when(controller.stopCamera()).thenThrow(QrUnsupportedVersionException(0)); + await tester.tap(find.byType(FloatingActionButton)); + await tester.pump(); + + (tester.widget(find.byType(QRView)) as QRView) + .onQRViewCreated(controller); + }); + testWidgets( + 'Check if _onQRViewCreated() is working fine when throws Exception', (tester) async { await tester.pumpWidget( MaterialApp( @@ -136,8 +212,10 @@ Future main() async { when(controller.scannedDataStream).thenAnswer((_) async* { yield Barcode('qr?orgId=1&scan', BarcodeFormat.qrcode, null); }); - when(controller.stopCamera()).thenThrow(Exception()); + // when(controller.stopCamera()) + // .thenThrow(Exception({"errorType": "error"})); + when(controller.stopCamera()).thenThrow(Exception(0)); await tester.tap(find.byType(FloatingActionButton)); await tester.pump(); diff --git a/test/widget_tests/after_auth_screens/app_settings/app_setting_page_test.dart b/test/widget_tests/after_auth_screens/app_settings/app_setting_page_test.dart index 0c95f08b4..6a140eb3a 100644 --- a/test/widget_tests/after_auth_screens/app_settings/app_setting_page_test.dart +++ b/test/widget_tests/after_auth_screens/app_settings/app_setting_page_test.dart @@ -1,16 +1,11 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:hive/hive.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'package:talawa/constants/constants.dart'; import 'package:talawa/constants/custom_theme.dart'; import 'package:talawa/models/language/language_model.dart'; -import 'package:talawa/models/organization/org_info.dart'; -import 'package:talawa/models/user/user_info.dart'; import 'package:talawa/router.dart' as router; import 'package:talawa/services/graphql_config.dart'; import 'package:talawa/services/navigation_service.dart'; @@ -90,14 +85,14 @@ Future main() async { locator().test(); locator().test(); - final Directory dir = Directory('temporaryPath'); - Hive - ..init(dir.path) - ..registerAdapter(UserAdapter()) - ..registerAdapter(OrgInfoAdapter()); - await Hive.openBox('currentUser'); - await Hive.openBox('currentOrg'); - await Hive.openBox('url'); + // final Directory dir = Directory('temporaryPath'); + // Hive + // ..init(dir.path) + // ..registerAdapter(UserAdapter()) + // ..registerAdapter(OrgInfoAdapter()); + // await Hive.openBox('currentUser'); + // await Hive.openBox('currentOrg'); + // await Hive.openBox('url'); group('Setting Page Screen Widget Test in dark mode', () { testWidgets("Testing if Settings Screen shows up", (tester) async { await tester.pumpWidget(createChangePassScreenDark()); diff --git a/test/widget_tests/widgets/custom_alert_dialog_test.dart b/test/widget_tests/widgets/custom_alert_dialog_test.dart index ed316491e..2bfe8769c 100644 --- a/test/widget_tests/widgets/custom_alert_dialog_test.dart +++ b/test/widget_tests/widgets/custom_alert_dialog_test.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; -// import 'package:talawa/locator.dart' as loc; import 'package:talawa/services/navigation_service.dart'; import 'package:talawa/services/size_config.dart'; import 'package:talawa/utils/app_localization.dart'; diff --git a/test/widget_tests/widgets/talawa_error_dialog_test.dart b/test/widget_tests/widgets/talawa_error_dialog_test.dart new file mode 100644 index 000000000..87cae0abf --- /dev/null +++ b/test/widget_tests/widgets/talawa_error_dialog_test.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:talawa/services/navigation_service.dart'; +import 'package:talawa/services/size_config.dart'; +import 'package:talawa/utils/app_localization.dart'; +import 'package:talawa/widgets/talawa_error_dialog.dart'; + +import '../../helpers/test_locator.dart'; + +Widget createTalawaErrorWidget({ + bool reverse = false, + String? dialogTitle, + bool passSecondaryFunc = true, +}) { + return MaterialApp( + navigatorKey: locator().navigatorKey, + navigatorObservers: [], + locale: const Locale('en'), + supportedLocales: [ + const Locale('en', 'US'), + const Locale('es', 'ES'), + const Locale('fr', 'FR'), + const Locale('hi', 'IN'), + const Locale('zh', 'CN'), + const Locale('de', 'DE'), + const Locale('ja', 'JP'), + const Locale('pt', 'PT'), + ], + localizationsDelegates: [ + const AppLocalizationsDelegate(isTest: true), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + home: Scaffold( + body: TextButton( + child: const Text('Open'), + onPressed: () { + navigationService.showTalawaErrorDialog("Test Error"); + }, + ), + ), + ); +} + +void main() { + SizeConfig().test(); + testSetupLocator(); + group('Test for TalawaErrorWidget', () { + testWidgets('Check if the Error Widget shows up', (tester) async { + await tester.pumpWidget(createTalawaErrorWidget()); + await tester.pump(); + await tester.tap(find.textContaining('Open')); + await tester.pump(const Duration(seconds: 1)); + + expect(find.byType(TalawaErrorDialog), findsOneWidget); + }); + + testWidgets('Check if the close Button is working', (tester) async { + await tester.pumpWidget(createTalawaErrorWidget()); + await tester.pump(); + + await tester.tap(find.textContaining('Open')); + await tester.pump(const Duration(seconds: 1)); + + await tester.tap(find.textContaining('Close')); + await tester.pump(const Duration(seconds: 1)); + + expect(find.byType(TalawaErrorDialog), findsNothing); + }); + }); +} diff --git a/test/widget_tests/widgets/talawa_error_widget_test.dart b/test/widget_tests/widgets/talawa_error_widget_test.dart new file mode 100644 index 000000000..cf8d3203b --- /dev/null +++ b/test/widget_tests/widgets/talawa_error_widget_test.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:talawa/services/navigation_service.dart'; +import 'package:talawa/services/size_config.dart'; +import 'package:talawa/utils/app_localization.dart'; +import 'package:talawa/widgets/talawa_error_widget.dart'; + +import '../../helpers/test_locator.dart'; + +Widget createTalawaErrorWidget({ + bool reverse = false, + String? dialogTitle, + bool passSecondaryFunc = true, +}) { + return MaterialApp( + navigatorKey: locator().navigatorKey, + navigatorObservers: [], + locale: const Locale('en'), + supportedLocales: [ + const Locale('en', 'US'), + const Locale('es', 'ES'), + const Locale('fr', 'FR'), + const Locale('hi', 'IN'), + const Locale('zh', 'CN'), + const Locale('de', 'DE'), + const Locale('ja', 'JP'), + const Locale('pt', 'PT'), + ], + localizationsDelegates: [ + const AppLocalizationsDelegate(isTest: true), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + home: Scaffold( + body: TextButton( + child: const Text('Open'), + onPressed: () { + navigationService.showTalawaErrorWidget("Test Error"); + }, + ), + ), + ); +} + +void main() { + SizeConfig().test(); + testSetupLocator(); + group('Test for TalawaErrorWidget', () { + testWidgets('Check if the Snackbar shows up', (tester) async { + await tester.pumpWidget(createTalawaErrorWidget()); + await tester.pump(); + await tester.tap(find.textContaining('Open')); + await tester.pump(const Duration(seconds: 1)); + expect(find.byType(TalawaErrorWidget), findsOneWidget); + }); + }); +}