Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrating qr_code_scanner to mobile_scanner in talawa mobile #2717

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
1 change: 1 addition & 0 deletions lib/locator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ final actionHandlerService = locator<ActionHandlerService>();
///
/// **returns**:
/// None

Future<void> setupLocator() async {
locator.registerSingleton(DataBaseMutationFunctions());

Expand Down
16 changes: 12 additions & 4 deletions lib/splash_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@ import 'package:talawa/utils/app_localization.dart';

/// This widget return the SplashScreen. Splash Screen is the first screen that we see when we run our application. It is also known as Launch Screen.
class SplashScreen extends StatefulWidget {
const SplashScreen({required Key key, this.mainScreenIndex = 0})
: super(key: key);
const SplashScreen({
required Key key,
this.mainScreenIndex = 0,
this.isTesting = false,
}) : super(key: key);

/// This is required if url requires us to push different Screen to Home Screen.
final int mainScreenIndex;

/// for testing purpose.
final bool isTesting;

@override
_SplashScreenState createState() => _SplashScreenState();
}
Expand Down Expand Up @@ -206,6 +212,8 @@ class _SplashScreenState extends State<SplashScreen> {
/// **returns**:
/// None
void _handleUserLogIn(bool userLoggedIn) {
print(widget.isTesting);
if (widget.isTesting) return;
Future.delayed(const Duration(milliseconds: 750)).then((value) async {
final pushReplacementScreen = navigationService.pushReplacementScreen;
if (!userLoggedIn) {
Expand Down Expand Up @@ -239,9 +247,9 @@ class _SplashScreenState extends State<SplashScreen> {
}

@override
void dispose() {
_sub.cancel();
Future<void> dispose() async {
super.dispose();
await _sub.cancel();
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
// import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:talawa/constants/routing_constants.dart';
import 'package:talawa/enums/enums.dart';
import 'package:talawa/locator.dart';
Expand Down
122 changes: 64 additions & 58 deletions lib/view_model/pre_auth_view_models/set_url_view_model.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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:mobile_scanner/mobile_scanner.dart';
import 'package:talawa/constants/app_strings.dart';
import 'package:talawa/enums/enums.dart';
import 'package:talawa/locator.dart';
Expand Down Expand Up @@ -56,6 +55,9 @@
/// qrValidator.
AutovalidateMode validate = AutovalidateMode.disabled;

/// qrController.
final MobileScannerController controller = MobileScannerController();

Comment on lines +58 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Manage controller lifecycle

Dispose of the MobileScannerController when it’s no longer needed to avoid memory leaks. For instance, add a cleanup method to this ViewModel or handle disposal if the user exits before any scan completes.

🧰 Tools
🪛 GitHub Actions: PR Workflow

[warning] File required formatting changes

/// This function initialises the variables.
///
/// **params**:
Expand Down Expand Up @@ -239,16 +241,41 @@
SizedBox(
height: 250,
width: 250,
child: QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
overlay: QrScannerOverlayShape(
borderRadius: 10,
borderLength: 20,
borderWidth: 10,
cutOutSize: 250,
),
/*overlayMargin: EdgeInsets.all(50)*/
child: MobileScanner(
fit: BoxFit.contain,
controller: controller,
errorBuilder: (ctx, error, _) {

Check warning on line 247 in lib/view_model/pre_auth_view_models/set_url_view_model.dart

View check run for this annotation

Codecov / codecov/patch

lib/view_model/pre_auth_view_models/set_url_view_model.dart#L247

Added line #L247 was not covered by tests
String errorMessage = '';
switch (error.errorCode) {
case MobileScannerErrorCode.controllerUninitialized:

Check warning on line 250 in lib/view_model/pre_auth_view_models/set_url_view_model.dart

View check run for this annotation

Codecov / codecov/patch

lib/view_model/pre_auth_view_models/set_url_view_model.dart#L249-L250

Added lines #L249 - L250 were not covered by tests
errorMessage = 'camera is not ready';
break;
case MobileScannerErrorCode.permissionDenied:

Check warning on line 253 in lib/view_model/pre_auth_view_models/set_url_view_model.dart

View check run for this annotation

Codecov / codecov/patch

lib/view_model/pre_auth_view_models/set_url_view_model.dart#L253

Added line #L253 was not covered by tests
errorMessage =
'Please provide camera permission to scan QR code';
break;
case MobileScannerErrorCode.unsupported:

Check warning on line 257 in lib/view_model/pre_auth_view_models/set_url_view_model.dart

View check run for this annotation

Codecov / codecov/patch

lib/view_model/pre_auth_view_models/set_url_view_model.dart#L257

Added line #L257 was not covered by tests
errorMessage =
'This device does not support scanning.';
break;
default:
errorMessage = 'An unkonwn error occurred';
}

WidgetsBinding.instance.addPostFrameCallback((_) {
navigationService.showTalawaErrorSnackBar(

Check warning on line 266 in lib/view_model/pre_auth_view_models/set_url_view_model.dart

View check run for this annotation

Codecov / codecov/patch

lib/view_model/pre_auth_view_models/set_url_view_model.dart#L265-L266

Added lines #L265 - L266 were not covered by tests
errorMessage,
MessageType.error,
);
});

return Center(
child: Text(

Check warning on line 273 in lib/view_model/pre_auth_view_models/set_url_view_model.dart

View check run for this annotation

Codecov / codecov/patch

lib/view_model/pre_auth_view_models/set_url_view_model.dart#L272-L273

Added lines #L272 - L273 were not covered by tests
errorMessage,
),
);
},
onDetect: _onQRViewCreated,
),
),
SizedBox(
Expand All @@ -269,56 +296,35 @@
/// This is the helper function which execute when the on QR view created.
///
/// **params**:
/// * `controller`: QRViewController
/// * `scanData`: BarcodeCapture
///
/// **returns**:
/// None

void _onQRViewCreated(QRViewController controller) {
controller.scannedDataStream.listen((scanData) {
/// if the scanData is not empty.
if (scanData.code!.isNotEmpty) {
print(scanData.code);
try {
final List<String> data = scanData.code!.split('?');
url.text = data[0];
final List<String> queries = data[1].split('&');
orgId = queries[0].split('=')[1];
Vibration.vibrate(duration: 100);
controller.stopCamera();
controller.dispose();
final box = Hive.box('url');
box.put(urlKey, url.text);
box.put(imageUrlKey, "${url.text}/talawa/");
graphqlConfig.getOrgUrl();
Navigator.pop(navigationService.navigatorKey.currentContext!);
navigationService.pushScreen('/selectOrg', arguments: orgId);
} on CameraException catch (e) {
debugPrint(e.toString());
navigationService.showTalawaErrorSnackBar(
"The Camera is not working",
MessageType.error,
);
} on QrEmbeddedImageException catch (e) {
debugPrint(e.toString());
navigationService.showTalawaErrorDialog(
"The QR is not Working",
MessageType.error,
);
} on QrUnsupportedVersionException catch (e) {
debugPrint(e.toString());
navigationService.showTalawaErrorDialog(
"This QR version is not Supported.",
MessageType.error,
);
} on Exception catch (e) {
debugPrint(e.toString());
navigationService.showTalawaErrorSnackBar(
"This QR is not for the App",
MessageType.error,
);
}
void _onQRViewCreated(BarcodeCapture scanData) {
if (scanData.raw != null && scanData.barcodes.isNotEmpty) {
final String code = scanData.barcodes.first.displayValue!;

Check warning on line 306 in lib/view_model/pre_auth_view_models/set_url_view_model.dart

View check run for this annotation

Codecov / codecov/patch

lib/view_model/pre_auth_view_models/set_url_view_model.dart#L304-L306

Added lines #L304 - L306 were not covered by tests
try {
final List<String> data = code.split('?');
url.text = data[0];
final List<String> queries = data[1].split('&');
orgId = queries[0].split('=')[1];
Vibration.vibrate(duration: 100);
controller.stop();
controller.dispose();
final box = Hive.box('url');
box.put(urlKey, url.text);
box.put(imageUrlKey, "${url.text}/talawa/");
graphqlConfig.getOrgUrl();
Navigator.pop(navigationService.navigatorKey.currentContext!);
navigationService.pushScreen('/selectOrg', arguments: orgId);
} on Exception catch (e) {
debugPrint(e.toString());
navigationService.showTalawaErrorSnackBar(

Check warning on line 323 in lib/view_model/pre_auth_view_models/set_url_view_model.dart

View check run for this annotation

Codecov / codecov/patch

lib/view_model/pre_auth_view_models/set_url_view_model.dart#L308-L323

Added lines #L308 - L323 were not covered by tests
"The Camera is not working",
MessageType.error,
);
}
});
}
Comment on lines +304 to +328
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling and URL validation

The current implementation has two issues:

  1. The error message "The Camera is not working" is misleading as the exception could be due to invalid QR code format.
  2. The URL parsing assumes a specific format without validation.

Consider this improved implementation:

   void _onQRViewCreated(BarcodeCapture scanData) {
     if (scanData.raw != null && scanData.barcodes.isNotEmpty) {
       final String code = scanData.barcodes.first.displayValue!;
       try {
+        // Validate QR code format
+        if (!code.contains('?') || !code.contains('=')) {
+          throw FormatException('Invalid QR code format');
+        }
         final List<String> data = code.split('?');
         url.text = data[0];
         final List<String> queries = data[1].split('&');
         orgId = queries[0].split('=')[1];
         Vibration.vibrate(duration: 100);
         controller.stop();
         controller.dispose();
         final box = Hive.box('url');
         box.put(urlKey, url.text);
         box.put(imageUrlKey, "${url.text}/talawa/");
         graphqlConfig.getOrgUrl();
         Navigator.pop(navigationService.navigatorKey.currentContext!);
         navigationService.pushScreen('/selectOrg', arguments: orgId);
-      } on Exception catch (e) {
+      } on FormatException catch (e) {
+        debugPrint(e.toString());
+        navigationService.showTalawaErrorSnackBar(
+          "Invalid QR code format. Please scan a valid organization QR code.",
+          MessageType.error,
+        );
+      } catch (e) {
         debugPrint(e.toString());
         navigationService.showTalawaErrorSnackBar(
-          "The Camera is not working",
+          "Failed to process QR code. Please try again.",
           MessageType.error,
         );
       }
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void _onQRViewCreated(BarcodeCapture scanData) {
if (scanData.raw != null && scanData.barcodes.isNotEmpty) {
final String code = scanData.barcodes.first.displayValue!;
try {
final List<String> data = code.split('?');
url.text = data[0];
final List<String> queries = data[1].split('&');
orgId = queries[0].split('=')[1];
Vibration.vibrate(duration: 100);
controller.stop();
controller.dispose();
final box = Hive.box('url');
box.put(urlKey, url.text);
box.put(imageUrlKey, "${url.text}/talawa/");
graphqlConfig.getOrgUrl();
Navigator.pop(navigationService.navigatorKey.currentContext!);
navigationService.pushScreen('/selectOrg', arguments: orgId);
} on Exception catch (e) {
debugPrint(e.toString());
navigationService.showTalawaErrorSnackBar(
"The Camera is not working",
MessageType.error,
);
}
});
}
void _onQRViewCreated(BarcodeCapture scanData) {
if (scanData.raw != null && scanData.barcodes.isNotEmpty) {
final String code = scanData.barcodes.first.displayValue!;
try {
// Validate QR code format
if (!code.contains('?') || !code.contains('=')) {
throw FormatException('Invalid QR code format');
}
final List<String> data = code.split('?');
url.text = data[0];
final List<String> queries = data[1].split('&');
orgId = queries[0].split('=')[1];
Vibration.vibrate(duration: 100);
controller.stop();
controller.dispose();
final box = Hive.box('url');
box.put(urlKey, url.text);
box.put(imageUrlKey, "${url.text}/talawa/");
graphqlConfig.getOrgUrl();
Navigator.pop(navigationService.navigatorKey.currentContext!);
navigationService.pushScreen('/selectOrg', arguments: orgId);
} on FormatException catch (e) {
debugPrint(e.toString());
navigationService.showTalawaErrorSnackBar(
"Invalid QR code format. Please scan a valid organization QR code.",
MessageType.error,
);
} catch (e) {
debugPrint(e.toString());
navigationService.showTalawaErrorSnackBar(
"Failed to process QR code. Please try again.",
MessageType.error,
);
}
}

}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ignore_for_file: talawa_api_doc, talawa_good_doc_comments
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';

import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:talawa/enums/enums.dart';
import 'package:talawa/locator.dart';
import 'package:talawa/services/graphql_config.dart';
Expand All @@ -13,17 +14,26 @@
import 'package:vibration/vibration.dart';

/// JoinOrganisationAfterAuth returns a widget for page to join the organization just after user authentication.
class JoinOrganisationAfterAuth extends StatelessWidget {
class JoinOrganisationAfterAuth extends StatefulWidget {
const JoinOrganisationAfterAuth({super.key, required this.orgId});

/// org identifier.
///
final String orgId;

@override
State<JoinOrganisationAfterAuth> createState() =>
_JoinOrganisationAfterAuthState();
}

class _JoinOrganisationAfterAuthState extends State<JoinOrganisationAfterAuth> {
/// qrController.
final MobileScannerController controller = MobileScannerController();

@override
Widget build(BuildContext context) {
return BaseView<SelectOrganizationViewModel>(
onModelReady: (model) => model.initialise(orgId),
onModelReady: (model) => model.initialise(widget.orgId),
builder: (context, model, child) {
return Scaffold(
key: const Key('JoinOrgScreen'),
Expand Down Expand Up @@ -112,18 +122,54 @@
SizedBox(
height: 250,
width: 250,
child: QRView(
key: model.qrKey,
onQRViewCreated: (controller) =>
_onQRViewCreated(controller, model),
overlay: QrScannerOverlayShape(
overlayColor: Theme.of(context).colorScheme.secondary,
borderRadius: 10,
borderLength: 20,
borderWidth: 10,
cutOutSize: 250,
),
/*overlayMargin: EdgeInsets.all(50)*/
child: MobileScanner(
fit: BoxFit.contain,
controller: controller,
errorBuilder: (ctx, error, _) {

Check warning on line 128 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L128

Added line #L128 was not covered by tests
String errorMessage = '';
switch (error.errorCode) {
case MobileScannerErrorCode.controllerUninitialized:

Check warning on line 131 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L130-L131

Added lines #L130 - L131 were not covered by tests
errorMessage = 'camera is not ready';
break;
case MobileScannerErrorCode.permissionDenied:

Check warning on line 134 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L134

Added line #L134 was not covered by tests
errorMessage =
'Please provide camera permission to scan QR code';
break;
case MobileScannerErrorCode.unsupported:

Check warning on line 138 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L138

Added line #L138 was not covered by tests
errorMessage =
'This device does not support scanning.';
break;
default:
errorMessage = 'An unkonwn error occurred';
}

WidgetsBinding.instance.addPostFrameCallback((_) {
navigationService.showTalawaErrorSnackBar(

Check warning on line 147 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L146-L147

Added lines #L146 - L147 were not covered by tests
errorMessage,
MessageType.error,
);
});

return Center(
child: Text(

Check warning on line 154 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L153-L154

Added lines #L153 - L154 were not covered by tests
errorMessage,
),
);
},
onDetect: (scanData) => _onQRViewCreated(scanData, model),
overlayBuilder: (context, constraints) {
return Container(

Check warning on line 161 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L159-L161

Added lines #L159 - L161 were not covered by tests
width: 250,
height: 250,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Theme.of(context).colorScheme.secondary,

Check warning on line 167 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L164-L167

Added lines #L164 - L167 were not covered by tests
width: 10,
),
),
);
},
),
),
SizedBox(
Expand Down Expand Up @@ -152,34 +198,33 @@
/// **returns**:
/// None
void _onQRViewCreated(
QRViewController controller,
BarcodeCapture scanData,
SelectOrganizationViewModel model,
) {
controller.scannedDataStream.listen((scanData) {
if (scanData.code!.isNotEmpty) {
print(scanData.code);
try {
final List<String> data = scanData.code!.split('?');
final String url = data[0];
Vibration.vibrate(duration: 100);
if (url == GraphqlConfig.orgURI) {
final List<String> queries = data[1].split('&');
model.orgId = queries[0].split('=')[1];
controller.stopCamera();
controller.dispose();
Navigator.pop(navigationService.navigatorKey.currentContext!);
model.initialise(model.orgId);
} else {
navigationService.showTalawaErrorSnackBar(
"Organisation on different server, logout and scan qr again",
MessageType.error,
);
}
} on Exception catch (e) {
print(e);
print('invalid app qr');
if (scanData.raw != null && scanData.barcodes.isNotEmpty) {
final String code = scanData.barcodes.first.displayValue!;
print(code);

Check warning on line 206 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L204-L206

Added lines #L204 - L206 were not covered by tests
try {
final List<String> data = code.split('?');
final String url = data[0];
Vibration.vibrate(duration: 100);
if (url == GraphqlConfig.orgURI) {
final List<String> queries = data[1].split('&');
model.orgId = queries[0].split('=')[1];
controller.stop();
controller.dispose();
Navigator.pop(navigationService.navigatorKey.currentContext!);
model.initialise(model.orgId);

Check warning on line 217 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L208-L217

Added lines #L208 - L217 were not covered by tests
} else {
navigationService.showTalawaErrorSnackBar(

Check warning on line 219 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L219

Added line #L219 was not covered by tests
"Organisation on different server, logout and scan qr again",
MessageType.error,
);
}
} on Exception catch (e) {
print(e);
print('invalid app qr');

Check warning on line 226 in lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart

View check run for this annotation

Codecov / codecov/patch

lib/views/after_auth_screens/join_org_after_auth/join_organisation_after_auth.dart#L224-L226

Added lines #L224 - L226 were not covered by tests
}
});
}
}
}
Loading
Loading