From 621771428b8a3894feabe9bb019a7e94a5fef920 Mon Sep 17 00:00:00 2001 From: shoaib-mohmad <shoaib.mohmad@tarento.com> Date: Thu, 10 Mar 2022 18:16:42 +0530 Subject: [PATCH] Completed login pin flow. --- lib/constants/api_endpoints.dart | 1 + lib/constants/app_constants.dart | 7 + lib/database/offline_model.dart | 62 ++- lib/l10n/app_en.arb | 18 +- lib/landing_page.dart | 21 +- lib/pages/application_details_page.dart | 25 +- lib/pages/create_pin_page.dart | 384 +++++++++++++++++++ lib/pages/home_page.dart | 81 ++-- lib/pages/inspection_summary.dart | 17 +- lib/pages/login_email_page.dart | 164 ++++++-- lib/pages/login_otp_page.dart | 64 +++- lib/repositories/application_repository.dart | 67 +++- lib/repositories/login_repository.dart | 61 ++- lib/routes.dart | 5 +- lib/services/login_service.dart | 28 +- lib/util/helper.dart | 20 + lib/util/notification_helper.dart | 46 +-- 17 files changed, 905 insertions(+), 166 deletions(-) create mode 100644 lib/pages/create_pin_page.dart diff --git a/lib/constants/api_endpoints.dart b/lib/constants/api_endpoints.dart index 15d99c0..ab3a522 100644 --- a/lib/constants/api_endpoints.dart +++ b/lib/constants/api_endpoints.dart @@ -2,6 +2,7 @@ class ApiUrl { static const baseUrl = 'https://smfdev.idc.tarento.com'; static const getOtp = '$baseUrl/api/user/requestOTP'; static const validateOtp = '$baseUrl/api/signIn'; + static const generatePin = '$baseUrl/api/user/generatePin'; static const updateUserDeviceToken = '$baseUrl/api/user/updateUserDeviceToken'; static const getAllApplications = '$baseUrl/api/forms/getAllApplications'; diff --git a/lib/constants/app_constants.dart b/lib/constants/app_constants.dart index 1109546..9bf19be 100644 --- a/lib/constants/app_constants.dart +++ b/lib/constants/app_constants.dart @@ -35,6 +35,7 @@ class AppDatabase { static const String name = 'smf_db'; static const String applicationsTable = 'applications'; static const String inspectionTable = 'inspections'; + static const String loginPinsTable = 'login_pins'; } class Storage { @@ -44,4 +45,10 @@ class Storage { static const String firstname = 'smf_user_first_name'; static const String lastname = 'smf_user_last_name'; static const String authtoken = 'smf_user_auth_token'; + static const String applicationId = 'smf_application_id'; +} + +class Inspector { + static const String leadInspector = 'lead_inspector'; + static const String assistantInspector = 'assistant_inspector'; } diff --git a/lib/database/offline_model.dart b/lib/database/offline_model.dart index bb6d3ec..ef73324 100644 --- a/lib/database/offline_model.dart +++ b/lib/database/offline_model.dart @@ -10,17 +10,23 @@ class OfflineModel { onCreate: (db, version) async { const String applicationsTable = AppDatabase.applicationsTable; const String inspectionTable = AppDatabase.inspectionTable; + const String loginPinsTable = AppDatabase.loginPinsTable; await db.execute('''CREATE TABLE $applicationsTable ( id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER NOT NULL, + username VARCHAR(64), application_data TEXT )'''); await db.execute('''CREATE TABLE $inspectionTable ( id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER NOT NULL, + inspector_type CHECK( inspector_type IN ('${Inspector.leadInspector}','${Inspector.assistantInspector}') ), inspection_data TEXT )'''); + await db.execute('''CREATE TABLE $loginPinsTable ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username VARCHAR(64), + pin VARCHAR(4) + )'''); }, version: 1); } @@ -33,19 +39,19 @@ class OfflineModel { ); } - static Future<Map> getApplications(int userId) async { + static Future<Map> getApplications(String username) async { final db = await OfflineModel.database(); - List<dynamic> whereArgs = [userId]; + List<dynamic> whereArgs = [username]; List<Map> rows = await db.query(AppDatabase.applicationsTable, - where: 'user_id = ?', orderBy: 'id DESC', whereArgs: whereArgs); + where: 'username = ?', orderBy: 'id DESC', whereArgs: whereArgs); return rows[0]; } - static Future<void> deleteApplications(int userId) async { + static Future<void> deleteApplications(String username) async { Database db = await OfflineModel.database(); - List<dynamic> whereArgs = [userId]; + List<dynamic> whereArgs = [username]; await db.delete(AppDatabase.applicationsTable, - where: 'user_id = ?', whereArgs: whereArgs); + where: 'username = ?', whereArgs: whereArgs); } static Future<void> saveInspection(Map<String, Object> data) async { @@ -57,17 +63,41 @@ class OfflineModel { ); } - static Future<List<Map<String, dynamic>>> getInspections(int userId) async { + static Future<List<Map<String, dynamic>>> getInspections() async { final db = await OfflineModel.database(); - List<dynamic> whereArgs = [userId]; - return db.query(AppDatabase.inspectionTable, - where: 'user_id = ?', whereArgs: whereArgs); + return db.query( + AppDatabase.inspectionTable, + ); } - static Future<void> deleteInspections(int userId) async { + static Future<void> deleteInspections() async { Database db = await OfflineModel.database(); - List<dynamic> whereArgs = [userId]; - await db.delete(AppDatabase.inspectionTable, - where: 'user_id = ?', whereArgs: whereArgs); + await db.delete( + AppDatabase.inspectionTable, + ); + } + + static Future<void> savePin(Map<String, Object> data) async { + final db = await OfflineModel.database(); + db.insert( + AppDatabase.loginPinsTable, + data, + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + + static Future<void> deletePin(String username) async { + Database db = await OfflineModel.database(); + List<dynamic> whereArgs = [username]; + await db.delete(AppDatabase.loginPinsTable, + where: 'username = ?', whereArgs: whereArgs); + } + + static Future<Map> getPinDetails(String pin) async { + final db = await OfflineModel.database(); + List<dynamic> whereArgs = [pin]; + List<Map> rows = await db.query(AppDatabase.loginPinsTable, + where: 'pin = ?', orderBy: 'id DESC', whereArgs: whereArgs); + return rows.isNotEmpty ? rows[0] : {}; } } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index acbfc98..659768f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -2,10 +2,22 @@ "login": "Login", "logout": "Logout", "emailId": "Email Id", + "enterHere": "Enter here", + "orEnterPin": "Or, Enter your 4 Digit Login Pin", + "enterPin": "Enter 4 Digit Login Pin", + "confirmPin": "Confirm 4 Digit Login Pin", + "createPin": "Create PIN", + "inValidPin": "Invalid PIN", "getOtp": "GET OTP", "enterOtp": "Enter OTP", "otpFieldDescription": "Enter the 6 digit OTP sent to your email address.", + "pleaseEnterEmail": "Please enter email id", + "pleaseEnterPin": "Please enter pin to proceed", + "pleaseMatchPin": "Please match pin to proceed", + "pleaseEnterValidPin": "Please enter valid pin to proceed", + "pinGeneratedMessage": "Pin generated successfully, please login", "signIn": "SIGN IN", + "goBack": "Go back", "goBackText": "Go back, re-enter the email", "pendingApplications": "Pending applications", "today": "Today", @@ -19,13 +31,13 @@ "completed": "Completed", "sentForInspection": "Sent for inspection", "noApplications": "No applications at the moment", - "sessionExpiredMessage": "Your session has expired.", + "sessionExpiredMessage": "Your session has expired", "isGivenInformationCorrect": "Is the given information found correct?", "typeHere": "Type here", "cancel": "Cancel", "submit": "Submit", "actualValue": "Actual value(s)", - "pleaseConcentDisagree": "Please concent or disagree", + "pleaseConsentDisagree": "Please consent or disagree", "status": "Status", "scheduledOn": "Scheduled on", "inspetionCompletedOn": "Inspection completed on", @@ -33,5 +45,5 @@ "leadInspector": "Lead inspector", "assistingInspectors": "Assisting inspectors", "iDisagree": "I disagree", - "iConcent": "I concent" + "iConsent": "I consent" } \ No newline at end of file diff --git a/lib/landing_page.dart b/lib/landing_page.dart index 888d48f..2fbec39 100644 --- a/lib/landing_page.dart +++ b/lib/landing_page.dart @@ -9,12 +9,16 @@ import 'package:smf_mobile/repositories/form_repository.dart'; import 'package:smf_mobile/repositories/login_repository.dart'; import 'package:smf_mobile/repositories/user_repository.dart'; import 'package:smf_mobile/util/helper.dart'; +import 'package:smf_mobile/util/notification_helper.dart'; +// import 'package:smf_mobile/util/notification_helper.dart'; import 'constants/app_constants.dart'; import 'constants/app_urls.dart'; import 'constants/color_constants.dart'; import 'routes.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +// import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class LandingPage extends StatefulWidget { static const route = AppUrl.landingPage; @@ -27,10 +31,23 @@ class LandingPage extends StatefulWidget { context.findAncestorStateOfType<_LandingPageState>(); } +Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async { + print("Handling a background message..."); + // BuildContext context; + // if (message.notification != null) { + // String applicationId = message.data['applicationId'] ?? ''; + // String body = message.notification!.body.toString(); + // for (int i = 0; i < 10; i++) { + // NotificationHelper.scheduleNotification(DateTime.now(), 0, + // message.notification!.title.toString(), body, applicationId); + // } +} + class _LandingPageState extends State<LandingPage> { final client = HttpClient(); Locale _locale = const Locale('en', 'US'); bool _isTokenExpired = false; + // final _storage = const FlutterSecureStorage(); @override void initState() { @@ -43,7 +60,7 @@ class _LandingPageState extends State<LandingPage> { }); } - Future<dynamic> _initilizeApp() async { + Future<dynamic> _initilizeApp(context) async { _isTokenExpired = await Helper.isTokenExpired(); await Firebase.initializeApp(); // await Future.delayed(const Duration(microseconds: 100)); @@ -56,7 +73,7 @@ class _LandingPageState extends State<LandingPage> { color: AppColors.scaffoldBackground, child: FutureBuilder( // Initialize FlutterFire - future: _initilizeApp(), + future: _initilizeApp(context), builder: (context, AsyncSnapshot<dynamic> snapshot) { // Check for errors if (snapshot.hasData) { diff --git a/lib/pages/application_details_page.dart b/lib/pages/application_details_page.dart index d115763..f8553c1 100644 --- a/lib/pages/application_details_page.dart +++ b/lib/pages/application_details_page.dart @@ -17,6 +17,9 @@ import 'package:smf_mobile/widgets/lead_inspector_application_field.dart'; import 'package:smf_mobile/widgets/people_card.dart'; import 'package:smf_mobile/widgets/silverappbar_delegate.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'dart:async'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:smf_mobile/util/connectivity_helper.dart'; class ApplicationDetailsPage extends StatefulWidget { final Application application; @@ -31,6 +34,8 @@ class ApplicationDetailsPage extends StatefulWidget { class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> with SingleTickerProviderStateMixin { + Map _source = {ConnectivityResult.none: false}; + final MyConnectivity _connectivity = MyConnectivity.instance; final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); TabController? _tabController; late FormData _formData; @@ -51,10 +56,15 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> String _inspectionSummary = ''; String _errorMessage = ''; String _inspectionStatus = ''; + late int _userId; @override void initState() { super.initState(); + _connectivity.initialise(); + _connectivity.myStream.listen((source) { + setState(() => _source = source); + }); widget.application.dataObject.forEach((key, value) => _tabs.add(key)); _tabController = TabController(vsync: this, length: _tabs.length); _tabController!.addListener(_setActiveTabIndex); @@ -64,10 +74,10 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> Future<void> _checkInspectorRole() async { String id = await Helper.getUser(Storage.userId); - int userId = int.parse(id); + _userId = int.parse(id); if (widget.application.leadInspector.isNotEmpty) { _leadInspectorId = widget.application.leadInspector[0]; - if (widget.application.leadInspector[0] == userId) { + if (widget.application.leadInspector[0] == _userId) { setState(() { _isleadInspector = true; }); @@ -88,7 +98,7 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> 'name': '${widget.application.inspectors[i]['firstName']} ${widget.application.inspectors[i]['lastName']}', }); - if (widget.application.inspectors[i]['id'] == userId && + if (widget.application.inspectors[i]['id'] == _userId && !_isleadInspector) { _note = widget.application.inspectors[i]['comments'] ?? ''; _inspectionStatus = widget.application.inspectors[i]['status'] ?? false; @@ -191,6 +201,7 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> if (_isleadInspector) { Map data = { 'applicationId': widget.application.applicationId, + 'userId': _userId, 'dataObject': _data }; @@ -205,19 +216,20 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> } else { if (!_iConcent && !_iDisagree) { Helper.toastMessage( - AppLocalizations.of(context)!.pleaseConcentDisagree); + AppLocalizations.of(context)!.pleaseConsentDisagree); return; } try { Map data = { 'applicationId': widget.application.applicationId, + 'userId': _userId, 'agree': _iConcent, 'comments': _note }; final responseCode = await Provider.of<ApplicationRespository>(context, listen: false) - .submitConcent(data); + .submitConcent(Helper.isInternetConnected(_source), data); if (responseCode != 0) { Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => const InspectionCompletedPage())); @@ -256,6 +268,7 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> @override void dispose() { _tabController?.dispose(); + // _connectivity.disposeStream(); super.dispose(); } @@ -802,7 +815,7 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage> children: [ Text( AppLocalizations.of(context)! - .iConcent, + .iConsent, style: GoogleFonts.lato( color: _iConcent ? Colors.white diff --git a/lib/pages/create_pin_page.dart b/lib/pages/create_pin_page.dart new file mode 100644 index 0000000..ed1f45a --- /dev/null +++ b/lib/pages/create_pin_page.dart @@ -0,0 +1,384 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:otp_text_field/style.dart'; +import 'package:provider/provider.dart'; +import 'package:smf_mobile/constants/app_urls.dart'; +import 'package:smf_mobile/constants/color_constants.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:smf_mobile/pages/home_page.dart'; +import 'package:smf_mobile/pages/login_email_page.dart'; +import 'package:smf_mobile/pages/login_otp_page.dart'; +import 'package:smf_mobile/repositories/login_repository.dart'; +import 'package:smf_mobile/util/helper.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:otp_text_field/otp_field.dart'; +import 'package:unique_identifier/unique_identifier.dart'; + +class CreatePinPage extends StatefulWidget { + static const route = AppUrl.loginEmailPage; + + const CreatePinPage({Key? key}) : super(key: key); + @override + _CreatePinPageState createState() => _CreatePinPageState(); +} + +class _CreatePinPageState extends State<CreatePinPage> { + final TextEditingController _emailController = TextEditingController(); + final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); + String _errorMessage = ''; + late Locale locale; + String _pin1 = ''; + String _pin2 = ''; + + @override + void initState() { + super.initState(); + } + + Future<void> _generateOtp() async { + final email = _emailController.text.trim(); + if (email == '') { + Helper.toastMessage(AppLocalizations.of(context)!.pleaseEnterEmail); + return; + } + if (_pin1 == '' || _pin2 == '') { + Helper.toastMessage(AppLocalizations.of(context)!.pleaseEnterPin); + return; + } + if (_pin1.length < 4 || _pin2.length < 4) { + Helper.toastMessage(AppLocalizations.of(context)!.pleaseEnterValidPin); + return; + } + if (_pin1 != _pin2) { + Helper.toastMessage(AppLocalizations.of(context)!.pleaseMatchPin); + return; + } + SystemChannels.textInput.invokeMethod('TextInput.hide'); + try { + final responseCode = + await Provider.of<LoginRespository>(context, listen: false) + .getOtp(email.trim()); + if (responseCode == 200) { + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (context) => LoginOtpPage( + username: email, + pin: _pin1, + loginRequest: false, + ), + )); + } else { + _errorMessage = + Provider.of<LoginRespository>(context, listen: false).errorMessage; + Helper.toastMessage(_errorMessage); + } + } catch (err) { + throw Exception(err); + } + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + body: SingleChildScrollView( + reverse: true, + child: SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 60, bottom: 60), + child: Center( + child: Image.asset( + 'assets/images/logo.png', + // width: 50.0, + // height: 50.0, + ), + ), + ), + Container( + padding: + const EdgeInsets.only(left: 40, right: 40, bottom: 40), + margin: const EdgeInsets.only(left: 20, right: 20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: Colors.white, + boxShadow: const [ + BoxShadow( + color: AppColors.black16, + offset: Offset(0, 2), + blurRadius: 2) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(top: 40), + child: Align( + alignment: Alignment.center, + child: Text( + AppLocalizations.of(context)!.createPin, + textAlign: TextAlign.center, + style: GoogleFonts.lato( + color: AppColors.black87, + fontSize: 20, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w600, + height: 1.4), + ))), + Padding( + padding: const EdgeInsets.only( + top: 50, + ), + child: Text( + AppLocalizations.of(context)!.emailId, + textAlign: TextAlign.left, + style: GoogleFonts.lato( + color: AppColors.black87, + fontSize: 16, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w400, + height: 1.4), + )), + Container( + width: double.infinity, + margin: const EdgeInsets.only( + top: 0, + ), + child: Padding( + padding: const EdgeInsets.fromLTRB(0, 5, 0, 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10.0), + Container( + alignment: Alignment.centerLeft, + height: 50.0, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + border: + Border.all(color: AppColors.black16), + color: Colors.white, + ), + child: Focus( + child: TextFormField( + textCapitalization: + TextCapitalization.none, + textInputAction: TextInputAction.done, + controller: _emailController, + style: GoogleFonts.lato( + color: AppColors.black40, + fontSize: 16, + letterSpacing: + 0 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.normal, + height: + 1.5 /*PERCENT not supported*/ + ), + keyboardType: + TextInputType.emailAddress, + decoration: const InputDecoration( + contentPadding: EdgeInsets.fromLTRB( + 20.0, 0.0, 20.0, 0.0), + border: InputBorder.none, + hintText: 'Enter here', + hintStyle: TextStyle( + color: AppColors.black40, + fontSize: 14, + letterSpacing: + 0 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.normal, + height: + 1.5 /*PERCENT not supported*/ + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.transparent, + width: 2.0), + ), + ), + ), + ), + ) + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.only( + top: 20, + ), + child: Text( + AppLocalizations.of(context)!.enterPin, + textAlign: TextAlign.left, + style: GoogleFonts.lato( + color: AppColors.black87, + fontSize: 16, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w400, + height: 1.4), + )), + Container( + alignment: Alignment.centerLeft, + height: 30.0, + margin: const EdgeInsets.only(top: 5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + // border: + // Border.all(color: AppColors.black16), + color: Colors.white, + ), + child: OTPTextField( + length: 4, + width: MediaQuery.of(context).size.width / 2, + fieldWidth: 38, + style: const TextStyle(fontSize: 14), + textFieldAlignment: + MainAxisAlignment.spaceAround, + fieldStyle: FieldStyle.underline, + onCompleted: (pin) { + setState(() { + _pin1 = pin; + }); + }, + onChanged: (String? pin) { + if (pin?.length == 4) { + setState(() { + _pin1 = pin.toString(); + }); + } + }), + ), + Padding( + padding: const EdgeInsets.only( + top: 20, + ), + child: Text( + AppLocalizations.of(context)!.confirmPin, + textAlign: TextAlign.left, + style: GoogleFonts.lato( + color: AppColors.black87, + fontSize: 16, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w400, + height: 1.4), + )), + Container( + alignment: Alignment.centerLeft, + height: 30.0, + margin: const EdgeInsets.only(top: 5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + // border: + // Border.all(color: AppColors.black16), + color: Colors.white, + ), + child: OTPTextField( + length: 4, + width: MediaQuery.of(context).size.width / 2, + fieldWidth: 38, + style: const TextStyle(fontSize: 14), + textFieldAlignment: + MainAxisAlignment.spaceAround, + fieldStyle: FieldStyle.underline, + onCompleted: (pin) { + setState(() { + _pin2 = pin; + }); + }, + onChanged: (String? pin) { + if (pin?.length == 4) { + setState(() { + _pin2 = pin.toString(); + }); + } + }), + ), + Padding( + padding: const EdgeInsets.only(top: 40), + child: InkWell( + // ignore: avoid_print + onTap: () => _generateOtp(), + child: SizedBox( + width: + MediaQuery.of(context).size.width - 80, + height: 50, + child: Stack(children: <Widget>[ + Align( + alignment: Alignment.bottomCenter, + child: Container( + width: MediaQuery.of(context) + .size + .width - + 80, + height: 50, + decoration: BoxDecoration( + color: AppColors.primaryBlue, + borderRadius: + BorderRadius.circular(4), + ))), + Align( + alignment: Alignment.center, + child: Text( + AppLocalizations.of(context)! + .submit, + textAlign: TextAlign.center, + style: GoogleFonts.lato( + color: Colors.white, + fontSize: 17, + letterSpacing: + 0 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.normal, + height: + 1.5 /*PERCENT not supported*/ + ), + )), + ]))), + ), + const Padding( + padding: EdgeInsets.only( + top: 20, + ), + child: Divider( + color: AppColors.black16, + )), + InkWell( + onTap: () => Navigator.of(context) + .pushReplacement(MaterialPageRoute( + builder: (context) => const LoginEmailPage(), + )), + child: Container( + width: double.infinity, + padding: const EdgeInsets.only( + top: 10, + ), + child: Text( + AppLocalizations.of(context)!.goBack, + textAlign: TextAlign.center, + style: GoogleFonts.lato( + color: AppColors.primaryBlue, + fontSize: 14, + letterSpacing: + 0.5 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w700, + height: 1.4), + )), + ) + ]), + ) + ], + )), + )); + } +} diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 4636a74..370e9a8 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -1,9 +1,12 @@ +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:smf_mobile/constants/app_constants.dart'; import 'package:smf_mobile/constants/app_urls.dart'; import 'package:smf_mobile/constants/color_constants.dart'; import 'package:smf_mobile/models/application_model.dart'; +import 'package:smf_mobile/pages/application_details_page.dart'; import 'package:smf_mobile/pages/login_email_page.dart'; import 'package:smf_mobile/pages/past_applications.dart'; import 'package:smf_mobile/repositories/application_repository.dart'; @@ -19,26 +22,76 @@ import 'package:smf_mobile/util/connectivity_helper.dart'; class HomePage extends StatefulWidget { static const route = AppUrl.homePage; - + // final String applicationId; const HomePage({Key? key}) : super(key: key); @override _HomePageState createState() => _HomePageState(); } -class _HomePageState extends State<HomePage> { +class _HomePageState extends State<HomePage> with WidgetsBindingObserver { Map _source = {ConnectivityResult.none: false}; final MyConnectivity _connectivity = MyConnectivity.instance; List<Application> _allApplications = []; final List<Application> _pendingApplications = []; final List<Application> _upcomingApplications = []; final List<Application> _pastApplications = []; + bool _isInForeground = true; + @override void initState() { super.initState(); _connectivity.initialise(); _connectivity.myStream.listen((source) { - setState(() => _source = source); + setState(() { + _source = source; + }); }); + // print('applicationId:' + widget.applicationId); + WidgetsBinding.instance!.addObserver(this); + } + + void _checkNewApplication(String applicationId) { + print('applicationId $applicationId'); + if (applicationId != '') { + for (Application application in _allApplications) { + if (application.applicationId == applicationId) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ApplicationDetailsPage( + application: application, + )), + ); + return; + } + } + } + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + _isInForeground = state == AppLifecycleState.resumed; + if (_isInForeground) { + FirebaseMessaging.instance.getInitialMessage().then((message) { + if (message != null) { + // print('getInitialMessage: ${message.data}'); + _checkNewApplication(message.data['applicationId']); + } + }); + // setState(() {}); + // print('_isInForeground...'); + // Future.delayed(const Duration(milliseconds: 500), () { + // _checkNewApplication(); + // }); + } + } + + @override + void dispose() { + // WidgetsBinding.instance!.removeObserver(this); + // _connectivity.disposeStream(); + super.dispose(); } void _validateUser() async { @@ -51,31 +104,11 @@ class _HomePageState extends State<HomePage> { } } - bool _isInternetConnected() { - bool connected; - switch (_source.keys.toList()[0]) { - case ConnectivityResult.mobile: - print('connected to mobile...'); - connected = true; - break; - case ConnectivityResult.wifi: - print('connected to wifi...'); - connected = true; - break; - case ConnectivityResult.none: - default: - print('offline...'); - connected = false; - } - return connected; - } - Future<dynamic> _getApplications() async { - print('_getApplications...'); _validateUser(); _allApplications = await Provider.of<ApplicationRespository>(context, listen: false) - .getApplications(_isInternetConnected()); + .getApplications(Helper.isInternetConnected(_source)); String _errorMessage = Provider.of<ApplicationRespository>(context, listen: false) .errorMessage; diff --git a/lib/pages/inspection_summary.dart b/lib/pages/inspection_summary.dart index 8856f68..2fb38fb 100644 --- a/lib/pages/inspection_summary.dart +++ b/lib/pages/inspection_summary.dart @@ -12,6 +12,9 @@ import 'package:smf_mobile/util/helper.dart'; import 'package:smf_mobile/widgets/people_card.dart'; import 'inspection_completed.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'dart:async'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:smf_mobile/util/connectivity_helper.dart'; class InspectionSummaryPage extends StatefulWidget { static const route = AppUrl.inspectionSummary; @@ -32,6 +35,8 @@ class InspectionSummaryPage extends StatefulWidget { } class _InspectionSummaryPageState extends State<InspectionSummaryPage> { + Map _source = {ConnectivityResult.none: false}; + final MyConnectivity _connectivity = MyConnectivity.instance; final TextEditingController _summaryController = TextEditingController(); final List<Map> _inspectors = []; int _leadInspectorId = 0; @@ -44,9 +49,19 @@ class _InspectionSummaryPageState extends State<InspectionSummaryPage> { @override void initState() { super.initState(); + _connectivity.initialise(); + _connectivity.myStream.listen((source) { + setState(() => _source = source); + }); _populateApplicationInspectors(); } + // @override + // void dispose() { + // // _connectivity.disposeStream(); + // super.dispose(); + // } + Future<void> _populateApplicationInspectors() async { if (widget.leadInspector.isNotEmpty) { _leadInspectorId = widget.leadInspector[0]; @@ -109,7 +124,7 @@ class _InspectionSummaryPageState extends State<InspectionSummaryPage> { }; final responseCode = await Provider.of<ApplicationRespository>(context, listen: false) - .submitInspection(data); + .submitInspection(Helper.isInternetConnected(_source), data); if (responseCode != 0) { Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => const InspectionCompletedPage())); diff --git a/lib/pages/login_email_page.dart b/lib/pages/login_email_page.dart index e623aa3..f4c1ba7 100644 --- a/lib/pages/login_email_page.dart +++ b/lib/pages/login_email_page.dart @@ -1,16 +1,19 @@ -// import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:otp_text_field/style.dart'; import 'package:provider/provider.dart'; import 'package:smf_mobile/constants/app_urls.dart'; import 'package:smf_mobile/constants/color_constants.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:smf_mobile/database/offline_model.dart'; +import 'package:smf_mobile/pages/create_pin_page.dart'; +import 'package:smf_mobile/pages/home_page.dart'; import 'package:smf_mobile/pages/login_otp_page.dart'; import 'package:smf_mobile/repositories/login_repository.dart'; import 'package:smf_mobile/util/helper.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:smf_mobile/util/notification_helper.dart'; +import 'package:otp_text_field/otp_field.dart'; +import 'package:unique_identifier/unique_identifier.dart'; class LoginEmailPage extends StatefulWidget { static const route = AppUrl.loginEmailPage; @@ -23,45 +26,36 @@ class LoginEmailPage extends StatefulWidget { class _LoginEmailPageState extends State<LoginEmailPage> { final TextEditingController _emailController = TextEditingController(); final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); - FirebaseMessaging messaging = FirebaseMessaging.instance; String _errorMessage = ''; late Locale locale; + String _pin = ''; + late String _identifier; @override void initState() { super.initState(); - // _configureMessaging(); + initUniqueIdentifierState(); } - // _configureMessaging() async { - // NotificationSettings settings = await messaging.requestPermission( - // alert: true, - // announcement: false, - // badge: true, - // carPlay: false, - // criticalAlert: false, - // provisional: false, - // sound: true, - // ); - // print('User granted permission: ${settings.authorizationStatus}'); + Future<void> initUniqueIdentifierState() async { + String? identifier; + try { + identifier = await UniqueIdentifier.serial; + } on PlatformException { + identifier = 'Failed to get Unique Identifier'; + } - // FirebaseMessaging.onMessage.listen((RemoteMessage message) { - // if (message.notification != null) { - // var random = Random(); - // int notificationId = random.nextInt(999999); - // String body = - // message.notification!.body.toString() + message.data.toString(); - // NotificationHelper.scheduleNotification(context, DateTime.now(), - // notificationId, message.notification!.title.toString(), body); - // } - // print('Message data: $message'); - // }); - // } + if (!mounted) return; + + setState(() { + _identifier = identifier!; + }); + } Future<void> _generateOtp() async { - final email = _emailController.text; + final email = _emailController.text.trim(); if (email == '') { - Helper.toastMessage('Please enter email.'); + Helper.toastMessage(AppLocalizations.of(context)!.pleaseEnterEmail); return; } SystemChannels.textInput.invokeMethod('TextInput.hide'); @@ -71,7 +65,7 @@ class _LoginEmailPageState extends State<LoginEmailPage> { .getOtp(email.trim()); if (responseCode == 200) { Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (context) => const LoginOtpPage(), + builder: (context) => LoginOtpPage(username: email), )); } else { _errorMessage = @@ -83,6 +77,34 @@ class _LoginEmailPageState extends State<LoginEmailPage> { } } + Future<void> _validatePin() async { + print('_validatePin...'); + String pin = _pin; + try { + Map pinDetails = await OfflineModel.getPinDetails(pin); + print(pinDetails); + if (pinDetails['username'] != null) { + final responseCode = await Provider.of<LoginRespository>(context, + listen: false) + .validateOtp( + context, pinDetails['username'], '', _identifier, pin, false); + if (responseCode == 200) { + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (context) => const HomePage(), + )); + } else { + _errorMessage = Provider.of<LoginRespository>(context, listen: false) + .errorMessage; + Helper.toastMessage(_errorMessage); + } + } else { + Helper.toastMessage(AppLocalizations.of(context)!.inValidPin); + } + } catch (err) { + throw Exception(err); + } + } + @override void dispose() { super.dispose(); @@ -100,7 +122,7 @@ class _LoginEmailPageState extends State<LoginEmailPage> { child: Column( children: [ Container( - padding: const EdgeInsets.only(top: 100, bottom: 100), + padding: const EdgeInsets.only(top: 60, bottom: 60), child: Center( child: Image.asset( 'assets/images/logo.png', @@ -221,7 +243,7 @@ class _LoginEmailPageState extends State<LoginEmailPage> { ), ), Padding( - padding: const EdgeInsets.only(bottom: 80), + padding: const EdgeInsets.only(bottom: 10), child: InkWell( // ignore: avoid_print onTap: () => _generateOtp(), @@ -261,6 +283,82 @@ class _LoginEmailPageState extends State<LoginEmailPage> { )), ]))), ), + Padding( + padding: const EdgeInsets.only( + top: 20, + ), + child: Text( + AppLocalizations.of(context)!.orEnterPin, + textAlign: TextAlign.left, + style: GoogleFonts.lato( + color: AppColors.black87, + fontSize: 16, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w400, + height: 1.4), + )), + Container( + alignment: Alignment.centerLeft, + height: 30.0, + margin: const EdgeInsets.only(top: 5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + // border: + // Border.all(color: AppColors.black16), + color: Colors.white, + ), + child: OTPTextField( + length: 4, + width: MediaQuery.of(context).size.width / 2, + fieldWidth: 38, + style: const TextStyle(fontSize: 14), + textFieldAlignment: + MainAxisAlignment.spaceAround, + fieldStyle: FieldStyle.underline, + onCompleted: (pin) { + setState(() { + _pin = pin; + }); + }, + onChanged: (String? pin) { + if (pin?.length == 4) { + setState(() { + _pin = pin.toString(); + }); + _validatePin(); + } + }), + ), + const Padding( + padding: EdgeInsets.only( + top: 20, + ), + child: Divider( + color: AppColors.black16, + )), + InkWell( + onTap: () => Navigator.of(context) + .pushReplacement(MaterialPageRoute( + builder: (context) => const CreatePinPage(), + )), + child: Container( + width: double.infinity, + padding: const EdgeInsets.only( + top: 10, + ), + child: Text( + AppLocalizations.of(context)!.createPin, + textAlign: TextAlign.center, + style: GoogleFonts.lato( + color: AppColors.primaryBlue, + fontSize: 14, + letterSpacing: + 0.5 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w700, + height: 1.4), + )), + ) ]), ) ], diff --git a/lib/pages/login_otp_page.dart b/lib/pages/login_otp_page.dart index 4423329..ec000e7 100644 --- a/lib/pages/login_otp_page.dart +++ b/lib/pages/login_otp_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smf_mobile/constants/app_urls.dart'; import 'package:smf_mobile/constants/color_constants.dart'; +import 'package:smf_mobile/database/offline_model.dart'; import 'package:smf_mobile/pages/home_page.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:smf_mobile/pages/login_email_page.dart'; @@ -16,7 +17,16 @@ import 'package:flutter/services.dart'; class LoginOtpPage extends StatefulWidget { static const route = AppUrl.loginOtpPage; - const LoginOtpPage({Key? key}) : super(key: key); + final String username; + final String pin; + final bool loginRequest; + + const LoginOtpPage({ + Key? key, + required this.username, + this.pin = '', + this.loginRequest = true, + }) : super(key: key); @override _LoginOtpPageState createState() => _LoginOtpPageState(); } @@ -51,17 +61,40 @@ class _LoginOtpPageState extends State<LoginOtpPage> { Future<void> _validateOtp() async { String otp = _otp; try { - final responseCode = - await Provider.of<LoginRespository>(context, listen: false) - .validateOtp(context, otp, _identifier); - if (responseCode == 200) { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (context) => const HomePage(), - )); + if (widget.loginRequest) { + final responseCode = await Provider.of<LoginRespository>(context, + listen: false) + .validateOtp(context, widget.username, otp, _identifier, '', true); + if (responseCode == 200) { + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (context) => const HomePage(), + )); + } else { + _errorMessage = Provider.of<LoginRespository>(context, listen: false) + .errorMessage; + Helper.toastMessage(_errorMessage); + } } else { - _errorMessage = - Provider.of<LoginRespository>(context, listen: false).errorMessage; - Helper.toastMessage(_errorMessage); + final responseData = + await Provider.of<LoginRespository>(context, listen: false) + .generatePin(widget.username, widget.pin, otp); + if (responseData) { + Helper.toastMessage( + AppLocalizations.of(context)!.pinGeneratedMessage); + await OfflineModel.deletePin(widget.username); + Map<String, Object> data = { + 'username': widget.username, + 'pin': widget.pin + }; + await OfflineModel.savePin(data); + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (context) => const LoginEmailPage(), + )); + } else { + _errorMessage = Provider.of<LoginRespository>(context, listen: false) + .errorMessage; + Helper.toastMessage(_errorMessage); + } } } catch (err) { throw Exception(err); @@ -85,7 +118,7 @@ class _LoginOtpPageState extends State<LoginOtpPage> { child: Column( children: [ Container( - padding: const EdgeInsets.only(top: 100, bottom: 100), + padding: const EdgeInsets.only(top: 60, bottom: 60), child: Center( child: Image.asset( 'assets/images/logo.png', @@ -227,8 +260,11 @@ class _LoginOtpPageState extends State<LoginOtpPage> { Align( alignment: Alignment.center, child: Text( - AppLocalizations.of(context)! - .signIn, + widget.loginRequest + ? AppLocalizations.of(context)! + .signIn + : AppLocalizations.of(context)! + .submit, textAlign: TextAlign.center, style: GoogleFonts.lato( color: Colors.white, diff --git a/lib/repositories/application_repository.dart b/lib/repositories/application_repository.dart index 90b4687..d1d1fc7 100644 --- a/lib/repositories/application_repository.dart +++ b/lib/repositories/application_repository.dart @@ -6,7 +6,7 @@ import 'package:smf_mobile/database/offline_model.dart'; import 'package:smf_mobile/models/application_model.dart'; import 'package:smf_mobile/services/application_service.dart'; import 'package:smf_mobile/util/helper.dart'; -import 'dart:developer' as developer; +// import 'dart:developer' as developer; class ApplicationRespository with ChangeNotifier { late Map _data; @@ -14,20 +14,19 @@ class ApplicationRespository with ChangeNotifier { String _errorMessage = ''; Future<dynamic> getApplications(bool internetConnected) async { - String rawUserId = await Helper.getUser(Storage.userId); - int userId = int.parse(rawUserId); + String username = await Helper.getUser(Storage.username); try { if (internetConnected) { final request = await ApplicationService.getApplications(); _data = json.decode(request.body); Map<String, Object> data = { - 'user_id': userId, + 'username': username, 'application_data': request.body }; - await OfflineModel.deleteApplications(userId); + await OfflineModel.deleteApplications(username); await OfflineModel.saveApplications(data); } else { - var applications = await OfflineModel.getApplications(userId); + var applications = await OfflineModel.getApplications(username); _data = json.decode(applications['application_data']); } } catch (_) { @@ -44,32 +43,60 @@ class ApplicationRespository with ChangeNotifier { return _applications; } - Future<dynamic> submitInspection(Map data) async { + Future<dynamic> submitInspection(bool internetConnected, Map data) async { try { - final request = await ApplicationService.submitInspection(data); - _data = json.decode(request.body); + if (!internetConnected) { + Map<String, Object> applicationData = { + 'inspector_type': Inspector.leadInspector, + 'inspection_data': data + }; + await OfflineModel.saveInspection(applicationData); + } else { + final request = await ApplicationService.submitInspection(data); + _data = json.decode(request.body); + } } catch (_) { return _; } - - if (_data['statusInfo']['statusCode'] != 200) { - _errorMessage = _data['statusInfo']['errorMessage']; + int statusCode; + if (internetConnected) { + if (_data['statusInfo']['statusCode'] != 200) { + _errorMessage = _data['statusInfo']['errorMessage']; + } + statusCode = _data['statusInfo']['statusCode']; + } else { + statusCode = 200; } - return _data['statusInfo']['statusCode']; + // var tData = await OfflineModel.getInspections(); + // developer.log(tData.toString()); + return statusCode; } - Future<dynamic> submitConcent(Map data) async { + Future<dynamic> submitConcent(bool internetConnected, Map data) async { try { - final request = await ApplicationService.submitConcent(data); - _data = json.decode(request.body); + if (!internetConnected) { + Map<String, Object> applicationData = { + 'inspector_type': Inspector.assistantInspector, + 'inspection_data': data + }; + await OfflineModel.saveInspection(applicationData); + } else { + final request = await ApplicationService.submitConcent(data); + _data = json.decode(request.body); + } } catch (_) { return _; } - - if (_data['statusInfo']['statusCode'] != 200) { - _errorMessage = _data['statusInfo']['errorMessage']; + int statusCode; + if (internetConnected) { + if (_data['statusInfo']['statusCode'] != 200) { + _errorMessage = _data['statusInfo']['errorMessage']; + } + statusCode = _data['statusInfo']['statusCode']; + } else { + statusCode = 200; } - return _data['statusInfo']['statusCode']; + return statusCode; } String get errorMessage => _errorMessage; diff --git a/lib/repositories/login_repository.dart b/lib/repositories/login_repository.dart index a6fa747..dd1adc3 100644 --- a/lib/repositories/login_repository.dart +++ b/lib/repositories/login_repository.dart @@ -2,11 +2,13 @@ import 'dart:convert'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/widgets.dart'; import 'package:smf_mobile/constants/app_constants.dart'; +import 'package:smf_mobile/landing_page.dart'; +// import 'package:smf_mobile/constants/app_urls.dart'; +// import 'package:smf_mobile/landing_page.dart'; import 'package:smf_mobile/models/login_model.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:smf_mobile/services/login_service.dart'; -import 'package:smf_mobile/util/helper.dart'; -import 'package:smf_mobile/util/notification_helper.dart'; +// import 'package:smf_mobile/util/notification_helper.dart'; class LoginRespository with ChangeNotifier { late Map _data; @@ -26,16 +28,38 @@ class LoginRespository with ChangeNotifier { if (_data['statusInfo']['statusCode'] != 200) { _errorMessage = _data['statusInfo']['errorMessage']; - } else { - _storage.write(key: 'username', value: username); } return _data['statusInfo']['statusCode']; } - Future<dynamic> validateOtp(context, String otp, String identifier) async { + Future<dynamic> generatePin(String username, String pin, String otp) async { try { - final username = await _storage.read(key: 'username'); - final request = await LoginService.validateOtp(username!, otp); + final request = await LoginService.generatePin(username, pin, otp); + _data = json.decode(request.body); + } catch (_) { + return _; + } + + if (_data['statusInfo']['statusCode'] != 200) { + _errorMessage = _data['statusInfo']['errorMessage']; + } + return _data['responseData']; + } + + Future<dynamic> validateOtp(context, String username, String otp, + String identifier, String pin, bool isOtp) async { + print('res validateOtp'); + try { + Map requestData = {}; + if (isOtp) { + requestData = {'username': username, 'otp': otp}; + } else { + requestData = { + 'username': username, + 'pin': pin, + }; + } + final request = await LoginService.validateOtp(requestData); _data = json.decode(request.body); } catch (_) { return _; @@ -59,7 +83,7 @@ class LoginRespository with ChangeNotifier { _data = json.decode(request.body); // print(_data); if (_data['statusInfo']['statusCode'] == 200) { - // print('_configureMessaging...'); + print('_configureMessaging...'); _configureMessaging(context); } }); @@ -67,7 +91,7 @@ class LoginRespository with ChangeNotifier { return _data['statusInfo']['statusCode']; } - _configureMessaging(context) async { + void _configureMessaging(context) async { NotificationSettings settings = await _firebaseMessaging.requestPermission( alert: true, announcement: false, @@ -77,19 +101,16 @@ class LoginRespository with ChangeNotifier { provisional: false, sound: true, ); - // print('User granted permission: ${settings.authorizationStatus}'); + print('User granted permission: ${settings.authorizationStatus}'); - FirebaseMessaging.onMessage.listen((RemoteMessage message) { - // print('message.notification...'); - if (message.notification != null) { - // int uniqueNotificationId = Helper.getUniqueId(); - String body = message.notification!.body.toString(); - NotificationHelper.scheduleNotification(context, DateTime.now(), 0, - message.notification!.title.toString(), body); - } - print('Message data: $message'); + FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); + + FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async { + // print('onMessageOpenedApp ${message.data}'); + // await _storage.write( + // key: Storage.applicationId, + // value: '${message.data['application_id']}'); }); - return; } Future<void> clearData() async { diff --git a/lib/routes.dart b/lib/routes.dart index 3e49c95..d2a5a57 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -20,7 +20,10 @@ class Routes { case AppUrl.loginOtpPage: return MaterialPageRoute( - settings: routeSettings, builder: (_) => const LoginOtpPage()); + settings: routeSettings, + builder: (_) => const LoginOtpPage( + username: '', + )); case AppUrl.homePage: return MaterialPageRoute( diff --git a/lib/services/login_service.dart b/lib/services/login_service.dart index 6df9e5a..83d6400 100644 --- a/lib/services/login_service.dart +++ b/lib/services/login_service.dart @@ -9,7 +9,10 @@ class LoginService extends BaseService { LoginService(HttpClient client) : super(client); static Future<dynamic> getOtp(String username) async { - Map requestData = {'username': username, 'isMobile': true}; + Map requestData = { + 'username': username, + 'isMobile': true, + }; var body = json.encode(requestData); Map<String, String> headers = await BaseService.getHeaders(); final response = @@ -17,12 +20,31 @@ class LoginService extends BaseService { return response; } - static Future<dynamic> validateOtp(String username, String otp) async { - Map requestData = {'username': username, 'otp': otp}; + static Future<dynamic> validateOtp(Map requestData) async { var body = json.encode(requestData); Map<String, String> headers = await BaseService.getHeaders(); final response = await http.post(Uri.parse(ApiUrl.validateOtp), headers: headers, body: body); + // developer.log(ApiUrl.validateOtp); + // developer.log(jsonEncode(requestData)); + // developer.log(response.body); + return response; + } + + static Future<dynamic> generatePin( + String username, String pin, String otp) async { + Map requestData = { + 'username': username, + 'pin': pin, + 'otp': otp, + }; + var body = json.encode(requestData); + Map<String, String> headers = await BaseService.getHeaders(); + final response = await http.post(Uri.parse(ApiUrl.generatePin), + headers: headers, body: body); + // developer.log(ApiUrl.generatePin); + // developer.log(jsonEncode(requestData)); + // developer.log(response.body); return response; } diff --git a/lib/util/helper.dart b/lib/util/helper.dart index ecebd84..65afc96 100644 --- a/lib/util/helper.dart +++ b/lib/util/helper.dart @@ -1,4 +1,5 @@ import 'dart:math'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:intl/intl.dart'; @@ -93,4 +94,23 @@ class Helper { // print(_inspectionStatus); return _inspectionStatus; } + + static bool isInternetConnected(source) { + bool connected; + switch (source.keys.toList()[0]) { + case ConnectivityResult.mobile: + // print('connected to mobile...'); + connected = true; + break; + case ConnectivityResult.wifi: + // print('connected to wifi...'); + connected = true; + break; + case ConnectivityResult.none: + default: + // print('offline...'); + connected = false; + } + return connected; + } } diff --git a/lib/util/notification_helper.dart b/lib/util/notification_helper.dart index c03623f..fbee26f 100644 --- a/lib/util/notification_helper.dart +++ b/lib/util/notification_helper.dart @@ -4,36 +4,36 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:smf_mobile/pages/home_page.dart'; +// import 'package:smf_mobile/pages/home_page.dart'; class NotificationHelper { - // static BuildContext get context => null; - static void onSelectNotification( - BuildContext context, - String payload, - ) { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (context) => const HomePage(), - )); - } + // static void onSelectNotification( + // BuildContext context, + // String payload, + // ) { + // Map data = jsonDecode(payload); + // print('Application ID 2: ' + data['applicationId']); + // } static Future<void> scheduleNotification( - BuildContext context, + // BuildContext context, DateTime scheduledNotificationDateTime, int notificationId, String title, - String notes) async { + String notes, + String applicationId) async { FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); - var initializationSettingsAndroid = - const AndroidInitializationSettings('flutter_devs'); - var initializationSettingsIOs = const IOSInitializationSettings(); - var initSetttings = InitializationSettings( - android: initializationSettingsAndroid, iOS: initializationSettingsIOs); - flutterLocalNotificationsPlugin.initialize(initSetttings, - onSelectNotification: (payload) async { - onSelectNotification(context, payload!); - }); + // var initializationSettingsAndroid = + // const AndroidInitializationSettings('flutter_devs'); + // var initializationSettingsIOs = const IOSInitializationSettings(); + // var initSetttings = InitializationSettings( + // android: initializationSettingsAndroid, iOS: initializationSettingsIOs); + // flutterLocalNotificationsPlugin.initialize(initSetttings, + // onSelectNotification: (payload) async { + // // print('payload: $payload'); + // // onSelectNotification(context, payload!); + // }); var androidPlatformChannelSpecifics = const AndroidNotificationDetails( 'channel id', @@ -43,7 +43,7 @@ class NotificationHelper { enableLights: true, playSound: true, // sound: RawResourceAndroidNotificationSound('mixkit_happy_bell'), - largeIcon: DrawableResourceAndroidBitmap('flutter_devs'), + // largeIcon: DrawableResourceAndroidBitmap('flutter_devs'), styleInformation: BigTextStyleInformation(''), ); @@ -52,7 +52,7 @@ class NotificationHelper { android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics); - Map payload = {}; + Map payload = {'applicationId': applicationId}; // ignore: deprecated_member_use await flutterLocalNotificationsPlugin.schedule(notificationId, title, notes, scheduledNotificationDateTime, platformChannelSpecifics, -- GitLab