diff --git a/refilc/lib/api/login.dart b/refilc/lib/api/login.dart index 1074dff..6db0229 100644 --- a/refilc/lib/api/login.dart +++ b/refilc/lib/api/login.dart @@ -67,6 +67,7 @@ Future loginAPI({ address: '1117 Budapest, Gábor Dénes utca 4.', ), role: Role.parent, + refreshToken: '', ); if (onLogin != null) onLogin(user); @@ -130,6 +131,7 @@ Future loginAPI({ password: password, instituteCode: instituteCode, )); + if (res != null) { if (res.containsKey("error")) { if (res["error"] == "invalid_grant") { @@ -151,6 +153,7 @@ Future loginAPI({ name: student.name, student: student, role: JwtUtils.getRoleFromJWT(res["access_token"])!, + refreshToken: '', ); if (onLogin != null) onLogin(user); @@ -209,6 +212,9 @@ Future newLoginAPI({ void Function()? onSuccess, }) async { // actual login (token grant) logic + Provider.of(context, listen: false).userAgent = + Provider.of(context, listen: false).config.userAgent; + Map headers = { "content-type": "application/x-www-form-urlencoded; charset=UTF-8", "accept": "*/*", @@ -216,12 +222,12 @@ Future newLoginAPI({ }; Map? res = await Provider.of(context, listen: false) - .postAPI(KretaAPI.login, autoHeader: false, headers: headers, body: { + .postAPI(KretaAPI.login, headers: headers, body: { "code": code, "code_verifier": "DSpuqj_HhDX4wzQIbtn8lr8NLE5wEi1iVLMtMK0jY6c", "redirect_uri": "https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect", - "client_id": "kreta-ellenorzo-student-mobile-ios", + "client_id": KretaAPI.clientId, "grant_type": "authorization_code", }); @@ -236,13 +242,12 @@ Future newLoginAPI({ return; } } else { - // print("MUKODIK GECI"); - // print("ACCESS TOKEN: ${res["access_token"]}"); if (res.containsKey("access_token")) { - print(JwtUtils.decodeJwt(res["access_token"])); try { Provider.of(context, listen: false).accessToken = res["access_token"]; + Provider.of(context, listen: false).refreshToken = + res["refresh_token"]; String instituteCode = JwtUtils.getInstituteFromJWT(res["access_token"])!; @@ -261,6 +266,7 @@ Future newLoginAPI({ name: student.name, student: student, role: role, + refreshToken: res["refresh_token"], ); if (onLogin != null) onLogin(user); diff --git a/refilc/lib/app.dart b/refilc/lib/app.dart index ef2207d..7a94f25 100644 --- a/refilc/lib/app.dart +++ b/refilc/lib/app.dart @@ -81,7 +81,8 @@ class App extends StatelessWidget { CorePalette? corePalette; final status = StatusProvider(); - final kreta = KretaClient(user: user, settings: settings, status: status); + final kreta = KretaClient( + user: user, settings: settings, database: database, status: status); final timetable = TimetableProvider(user: user, database: database, kreta: kreta); final premium = PlusProvider(settings: settings); diff --git a/refilc/lib/database/init.dart b/refilc/lib/database/init.dart index 84d4444..cde1c14 100644 --- a/refilc/lib/database/init.dart +++ b/refilc/lib/database/init.dart @@ -66,6 +66,7 @@ const usersDB = DatabaseStruct("users", { "institute_code": String, "student": String, "role": int, "nickname": String, "picture": String, // premium only (it's now plus btw) "grade_streak": int, + "refresh_token": String, }); const userDataDB = DatabaseStruct("user_data", { "id": String, "grades": String, "timetable": String, "exams": String, @@ -138,7 +139,8 @@ Future initDB(DatabaseProvider database) async { "role": 0, "nickname": "", "picture": "", - "grade_streak": 0 + "grade_streak": 0, + "refresh_token": "", }, ); await migrateDB(db, struct: userDataDB, defaultValues: { diff --git a/refilc/lib/helpers/notification_helper.dart b/refilc/lib/helpers/notification_helper.dart index 4313fad..f99ebfe 100644 --- a/refilc/lib/helpers/notification_helper.dart +++ b/refilc/lib/helpers/notification_helper.dart @@ -71,9 +71,11 @@ class NotificationsHelper { // Refresh kreta login for current user final status = StatusProvider(); KretaClient kretaClientForUser = KretaClient( - user: userProviderForUser, - settings: settingsProvider, - status: status); + user: userProviderForUser, + settings: settingsProvider, + database: database, + status: status, + ); await kretaClientForUser.refreshLogin(); // Process notifications for current user diff --git a/refilc/lib/models/user.dart b/refilc/lib/models/user.dart index 83d15d3..1b76ec2 100644 --- a/refilc/lib/models/user.dart +++ b/refilc/lib/models/user.dart @@ -17,6 +17,8 @@ class User { String nickname; String picture; int gradeStreak; + // new login method + String refreshToken; String get displayName => nickname != '' ? nickname : name; bool get hasStreak => gradeStreak > 0; @@ -32,6 +34,7 @@ class User { this.nickname = "", this.picture = "", this.gradeStreak = 0, + required this.refreshToken, }) { if (id != null) { this.id = id; @@ -61,6 +64,7 @@ class User { nickname: map["nickname"] ?? "", picture: map["picture"] ?? "", gradeStreak: map["grade_streak"] ?? 0, + refreshToken: map["refresh_token"] ?? "", ); } @@ -75,6 +79,8 @@ class User { "role": role.index, "nickname": nickname, "picture": picture, + "grade_streak": gradeStreak, + "refresh_token": refreshToken, }; } diff --git a/refilc_kreta_api/lib/client/api.dart b/refilc_kreta_api/lib/client/api.dart index aea3b85..d689d64 100644 --- a/refilc_kreta_api/lib/client/api.dart +++ b/refilc_kreta_api/lib/client/api.dart @@ -5,7 +5,7 @@ class KretaAPI { static const login = BaseKreta.kretaIdp + KretaApiEndpoints.token; static const logout = BaseKreta.kretaIdp + KretaApiEndpoints.revoke; static const nonce = BaseKreta.kretaIdp + KretaApiEndpoints.nonce; - static const clientId = "kreta-ellenorzo-mobile-android"; + static const clientId = "kreta-ellenorzo-student-mobile-ios"; // ELLENORZO API static String notes(String iss) => diff --git a/refilc_kreta_api/lib/client/client.dart b/refilc_kreta_api/lib/client/client.dart index b075ec1..a198731 100644 --- a/refilc_kreta_api/lib/client/client.dart +++ b/refilc_kreta_api/lib/client/client.dart @@ -1,15 +1,16 @@ -// ignore_for_file: avoid_print +// ignore_for_file: avoid_print, use_build_context_synchronously import 'dart:convert'; import 'dart:io'; -import 'package:refilc/api/login.dart'; -import 'package:refilc/api/nonce.dart'; +// import 'package:refilc/api/login.dart'; +// import 'package:refilc/api/nonce.dart'; +import 'package:refilc/api/providers/database_provider.dart'; import 'package:refilc/api/providers/user_provider.dart'; import 'package:refilc/api/providers/status_provider.dart'; import 'package:refilc/models/settings.dart'; import 'package:refilc/models/user.dart'; -import 'package:refilc/utils/jwt.dart'; +// import 'package:refilc/utils/jwt.dart'; import 'package:refilc_kreta_api/client/api.dart'; import 'package:http/http.dart' as http; import 'package:http/io_client.dart' as http; @@ -24,6 +25,7 @@ class KretaClient { late final SettingsProvider _settings; late final UserProvider _user; + late final DatabaseProvider _database; late final StatusProvider _status; bool _loginRefreshing = false; @@ -32,9 +34,11 @@ class KretaClient { this.accessToken, required SettingsProvider settings, required UserProvider user, + required DatabaseProvider database, required StatusProvider status, }) : _settings = settings, _user = user, + _database = database, _status = status, userAgent = settings.config.userAgent { var ioclient = HttpClient(); @@ -212,7 +216,7 @@ class KretaClient { res = await request.send(); if (res.statusCode == 401) { - headerMap.remove("authorization"); + headerMap.remove("authorization"); await refreshLogin(); } else { break; @@ -232,65 +236,74 @@ class KretaClient { } } - Future refreshLogin() async { - if (_loginRefreshing) return; + Future refreshLogin() async { + if (_loginRefreshing) return null; _loginRefreshing = true; User? loginUser = _user.user; - if (loginUser == null) return; + if (loginUser == null) return null; Map headers = { - "content-type": "application/x-www-form-urlencoded", + "content-type": "application/x-www-form-urlencoded; charset=UTF-8", + "accept": "*/*", + "user-agent": "eKretaStudent/264745 CFNetwork/1494.0.7 Darwin/23.4.0", }; - String nonceStr = await getAPI(KretaAPI.nonce, json: false); - Nonce nonce = - getNonce(nonceStr, loginUser.username, loginUser.instituteCode); - headers.addAll(nonce.header()); - if (_settings.presentationMode) { print("DEBUG: refreshLogin: ${loginUser.id}"); } else { print("DEBUG: refreshLogin: ${loginUser.id} ${loginUser.name}"); } - Map? loginRes = await postAPI( - KretaAPI.login, - headers: headers, - body: User.loginBody( - username: loginUser.username, - password: loginUser.password, - instituteCode: loginUser.instituteCode, - ), - ); + refreshToken ??= loginUser.refreshToken; - if (loginRes != null) { - if (loginRes.containsKey("access_token")) { - accessToken = loginRes["access_token"]; - } - if (loginRes.containsKey("refresh_token")) { - refreshToken = loginRes["refresh_token"]; - } - - // Update role - loginUser.role = - JwtUtils.getRoleFromJWT(accessToken ?? "") ?? Role.student; - } + print("REFRESH TOKEN BELOW"); + print(refreshToken); if (refreshToken != null) { - Map? refreshRes = await postAPI(KretaAPI.login, + print("REFRESHING LOGIN"); + Map? res = await postAPI(KretaAPI.login, headers: headers, body: User.refreshBody( - refreshToken: refreshToken!, - instituteCode: loginUser.instituteCode)); - if (refreshRes != null) { - if (refreshRes.containsKey("id_token")) { - idToken = refreshRes["id_token"]; + refreshToken: loginUser.refreshToken, + instituteCode: loginUser.instituteCode, + )); + print("REFRESH RESPONSE BELOW"); + print(res); + if (res != null) { + if (res.containsKey("error")) { + // remove user if refresh token expired + if (res["error"] == "invalid_grant") { + // remove user from app + _user.removeUser(loginUser.id); + await _database.store.removeUser(loginUser.id); + + // return error + return "refresh_token_expired"; + } } + + if (res.containsKey("access_token")) { + accessToken = res["access_token"]; + } + if (res.containsKey("refresh_token")) { + refreshToken = res["refresh_token"]; + loginUser.refreshToken = res["refresh_token"]; + _database.store.storeUser(loginUser); + _user.refresh(); + } + if (res.containsKey("id_token")) { + idToken = res["id_token"]; + } + _loginRefreshing = false; + } else { + _loginRefreshing = false; } + } else { + _loginRefreshing = false; } - _loginRefreshing = false; + return null; } Future logout() async { diff --git a/refilc_mobile_ui/lib/screens/login/kreten_login.dart b/refilc_mobile_ui/lib/screens/login/kreten_login.dart index 1b5cfd3..8396f8c 100644 --- a/refilc_mobile_ui/lib/screens/login/kreten_login.dart +++ b/refilc_mobile_ui/lib/screens/login/kreten_login.dart @@ -48,10 +48,6 @@ class _KretenLoginScreenState extends State { String code = requiredThings[0]; // String sessionState = requiredThings[1]; - debugPrint('url: $url'); - - print(code); - widget.onLogin(code); // Future.delayed(const Duration(milliseconds: 500), () { // Navigator.of(context).pop(); diff --git a/refilc_mobile_ui/lib/screens/login/login_screen.dart b/refilc_mobile_ui/lib/screens/login/login_screen.dart index 19c6004..e574253 100644 --- a/refilc_mobile_ui/lib/screens/login/login_screen.dart +++ b/refilc_mobile_ui/lib/screens/login/login_screen.dart @@ -341,59 +341,60 @@ class LoginScreenState extends State { ); } - void _loginAPI({required BuildContext context}) { - String username = usernameController.text; - String password = passwordController.text; + // void _loginAPI({required BuildContext context}) { + // String username = usernameController.text; + // String password = passwordController.text; - tempUsername = username; + // tempUsername = username; - if (username == "" || - password == "" || - schoolController.selectedSchool == null) { - return setState(() => _loginState = LoginState.missingFields); - } + // if (username == "" || + // password == "" || + // schoolController.selectedSchool == null) { + // return setState(() => _loginState = LoginState.missingFields); + // } - // ignore: no_leading_underscores_for_local_identifiers - void _callAPI() { - loginAPI( - username: username, - password: password, - instituteCode: schoolController.selectedSchool!.instituteCode, - context: context, - onLogin: (user) { - ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( - context: context, - brightness: Brightness.light, - content: Text("welcome".i18n.fill([user.name]), - overflow: TextOverflow.ellipsis), - )); - }, - onSuccess: () { - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - setSystemChrome(context); - Navigator.of(context).pushReplacementNamed("login_to_navigation"); - }).then( - (res) => setState(() { - // if (res == LoginState.invalidGrant && - // tempUsername.replaceAll(username, '').length <= 3) { - // tempUsername = username + ' '; - // Timer( - // const Duration(milliseconds: 500), - // () => _loginAPI(context: context), - // ); - // // _loginAPI(context: context); - // } else { - _loginState = res; - // } - }), - ); - } + // // ignore: no_leading_underscores_for_local_identifiers + // void _callAPI() { + // loginAPI( + // username: username, + // password: password, + // instituteCode: schoolController.selectedSchool!.instituteCode, + // context: context, + // onLogin: (user) { + // ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( + // context: context, + // brightness: Brightness.light, + // content: Text("welcome".i18n.fill([user.name]), + // overflow: TextOverflow.ellipsis), + // )); + // }, + // onSuccess: () { + // ScaffoldMessenger.of(context).hideCurrentSnackBar(); + // setSystemChrome(context); + // Navigator.of(context).pushReplacementNamed("login_to_navigation"); + // }).then( + // (res) => setState(() { + // // if (res == LoginState.invalidGrant && + // // tempUsername.replaceAll(username, '').length <= 3) { + // // tempUsername = username + ' '; + // // Timer( + // // const Duration(milliseconds: 500), + // // () => _loginAPI(context: context), + // // ); + // // // _loginAPI(context: context); + // // } else { + // _loginState = res; + // // } + // }), + // ); + // } - setState(() => _loginState = LoginState.inProgress); - _callAPI(); - } + // setState(() => _loginState = LoginState.inProgress); + // _callAPI(); + // } // new login api + // ignore: non_constant_identifier_names void _NewLoginAPI({required BuildContext context}) { String code = codeController.text; diff --git a/refilc_mobile_ui/lib/screens/login/login_screen_old.dart b/refilc_mobile_ui/lib/screens/login/login_screen_old.dart index 75c7d2a..aaeb615 100644 --- a/refilc_mobile_ui/lib/screens/login/login_screen_old.dart +++ b/refilc_mobile_ui/lib/screens/login/login_screen_old.dart @@ -1,10 +1,13 @@ // import 'dart:async'; +import 'package:refilc/api/client.dart'; import 'package:refilc/api/login.dart'; import 'package:refilc/theme/colors/colors.dart'; import 'package:refilc_mobile_ui/common/custom_snack_bar.dart'; import 'package:refilc_mobile_ui/common/system_chrome.dart'; -import 'package:refilc_mobile_ui/screens/login/kreten_login.dart'; +// import 'package:refilc_mobile_ui/screens/login/kreten_login.dart'; +import 'package:refilc_mobile_ui/screens/login/login_button.dart'; +import 'package:refilc_mobile_ui/screens/login/login_input.dart'; import 'package:refilc_mobile_ui/screens/login/school_input/school_input.dart'; import 'package:refilc_mobile_ui/screens/settings/privacy_view.dart'; import 'package:flutter/material.dart'; @@ -58,20 +61,20 @@ class LoginScreenState extends State { systemNavigationBarIconBrightness: Brightness.dark, )); - // FilcAPI.getSchools().then((schools) { - // if (schools != null) { - // schoolController.update(() { - // schoolController.schools = schools; - // }); - // } else { - // ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( - // content: Text("schools_error".i18n, - // style: const TextStyle(color: Colors.white)), - // backgroundColor: AppColors.of(context).red, - // context: context, - // )); - // } - // }); + FilcAPI.getSchools().then((schools) { + if (schools != null) { + schoolController.update(() { + schoolController.schools = schools; + }); + } else { + ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( + content: Text("schools_error".i18n, + style: const TextStyle(color: Colors.white)), + backgroundColor: AppColors.of(context).red, + context: context, + )); + } + }); } @override @@ -150,217 +153,217 @@ class LoginScreenState extends State { ), // kreten login button - GestureDetector( - onTap: () { - final NavigatorState navigator = Navigator.of(context); - navigator - .push( - MaterialPageRoute( - builder: (context) => KretenLoginScreen( - onLogin: (String code) { - codeController.text = code; - navigator.pop(); - }, - ), - ), - ) - .then((value) { - if (codeController.text != "") { - _NewLoginAPI(context: context); - } - }); - }, - child: Container( - width: MediaQuery.of(context).size.width * 0.75, - height: 50.0, - decoration: BoxDecoration( - // image: const DecorationImage( - // image: - // AssetImage('assets/images/btn_kreten_login.png'), - // fit: BoxFit.scaleDown, - // ), - borderRadius: BorderRadius.circular(12.0), - color: const Color(0xFF0097C1), - ), - padding: const EdgeInsets.only( - top: 5.0, left: 5.0, right: 5.0, bottom: 5.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - 'assets/images/btn_kreten_login.png', - ), - const SizedBox( - width: 10.0, - ), - Container( - width: 1.0, - height: 30.0, - color: Colors.white, - ), - const SizedBox( - width: 10.0, - ), - Text( - 'login_w_kreta_acc'.i18n, - textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 15.0, - ), - ), - ], - )), - ), - - const Spacer( - flex: 1, - ), - - // inputs - // Padding( - // padding: const EdgeInsets.only( - // left: 22.0, - // right: 22.0, - // top: 0.0, - // ), - // child: AutofillGroup( - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // // username - // Padding( - // padding: const EdgeInsets.only(bottom: 6.0), - // child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Expanded( - // child: Text( - // "username".i18n, - // maxLines: 1, - // style: TextStyle( - // color: AppColors.of(context).loginPrimary, - // fontWeight: FontWeight.w500, - // fontSize: 12.0, - // ), - // ), - // ), - // Expanded( - // child: Text( - // "usernameHint".i18n, - // maxLines: 1, - // textAlign: TextAlign.right, - // style: TextStyle( - // color: - // AppColors.of(context).loginSecondary, - // fontWeight: FontWeight.w500, - // fontSize: 12.0, - // ), - // ), - // ), - // ], - // ), + // GestureDetector( + // onTap: () { + // final NavigatorState navigator = Navigator.of(context); + // navigator + // .push( + // MaterialPageRoute( + // builder: (context) => KretenLoginScreen( + // onLogin: (String code) { + // codeController.text = code; + // navigator.pop(); + // }, // ), - // Padding( - // padding: const EdgeInsets.only(bottom: 12.0), - // child: LoginInput( - // style: LoginInputStyle.username, - // controller: usernameController, + // ), + // ) + // .then((value) { + // if (codeController.text != "") { + // _NewLoginAPI(context: context); + // } + // }); + // }, + // child: Container( + // width: MediaQuery.of(context).size.width * 0.75, + // height: 50.0, + // decoration: BoxDecoration( + // // image: const DecorationImage( + // // image: + // // AssetImage('assets/images/btn_kreten_login.png'), + // // fit: BoxFit.scaleDown, + // // ), + // borderRadius: BorderRadius.circular(12.0), + // color: const Color(0xFF0097C1), + // ), + // padding: const EdgeInsets.only( + // top: 5.0, left: 5.0, right: 5.0, bottom: 5.0), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Image.asset( + // 'assets/images/btn_kreten_login.png', // ), - // ), - - // // password - // Padding( - // padding: const EdgeInsets.only(bottom: 6.0), - // child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Expanded( - // child: Text( - // "password".i18n, - // maxLines: 1, - // style: TextStyle( - // color: AppColors.of(context).loginPrimary, - // fontWeight: FontWeight.w500, - // fontSize: 12.0, - // ), - // ), - // ), - // Expanded( - // child: Text( - // "passwordHint".i18n, - // maxLines: 1, - // textAlign: TextAlign.right, - // style: TextStyle( - // color: - // AppColors.of(context).loginSecondary, - // fontWeight: FontWeight.w500, - // fontSize: 12.0, - // ), - // ), - // ), - // ], + // const SizedBox( + // width: 10.0, // ), - // ), - // Padding( - // padding: const EdgeInsets.only(bottom: 12.0), - // child: LoginInput( - // style: LoginInputStyle.password, - // controller: passwordController, + // Container( + // width: 1.0, + // height: 30.0, + // color: Colors.white, // ), - // ), - - // // school - // Padding( - // padding: const EdgeInsets.only(bottom: 6.0), - // child: Text( - // "school".i18n, - // maxLines: 1, - // style: TextStyle( - // color: AppColors.of(context).loginPrimary, - // fontWeight: FontWeight.w500, - // fontSize: 12.0, + // const SizedBox( + // width: 10.0, + // ), + // Text( + // 'login_w_kreta_acc'.i18n, + // textAlign: TextAlign.center, + // style: const TextStyle( + // color: Colors.white, + // fontWeight: FontWeight.bold, + // fontSize: 15.0, // ), // ), - // ), - // SchoolInput( - // scroll: _scrollController, - // controller: schoolController, - // ), - // ], - // ), - // ), + // ], + // )), // ), - // login button - // Padding( - // padding: const EdgeInsets.only( - // top: 35.0, - // left: 22.0, - // right: 22.0, - // ), - // child: Visibility( - // visible: _loginState != LoginState.inProgress, - // replacement: const Padding( - // padding: EdgeInsets.symmetric(vertical: 6.0), - // child: CircularProgressIndicator( - // valueColor: - // AlwaysStoppedAnimation(Colors.white), - // ), - // ), - // child: LoginButton( - // child: Text("login".i18n, - // maxLines: 1, - // style: const TextStyle( - // fontWeight: FontWeight.bold, - // fontSize: 20.0, - // )), - // onPressed: () => _loginAPI(context: context), - // ), - // ), + // const Spacer( + // flex: 1, // ), + // inputs + Padding( + padding: const EdgeInsets.only( + left: 22.0, + right: 22.0, + top: 0.0, + ), + child: AutofillGroup( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // username + Padding( + padding: const EdgeInsets.only(bottom: 6.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "username".i18n, + maxLines: 1, + style: TextStyle( + color: AppColors.of(context).loginPrimary, + fontWeight: FontWeight.w500, + fontSize: 12.0, + ), + ), + ), + Expanded( + child: Text( + "usernameHint".i18n, + maxLines: 1, + textAlign: TextAlign.right, + style: TextStyle( + color: + AppColors.of(context).loginSecondary, + fontWeight: FontWeight.w500, + fontSize: 12.0, + ), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: LoginInput( + style: LoginInputStyle.username, + controller: usernameController, + ), + ), + + // password + Padding( + padding: const EdgeInsets.only(bottom: 6.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "password".i18n, + maxLines: 1, + style: TextStyle( + color: AppColors.of(context).loginPrimary, + fontWeight: FontWeight.w500, + fontSize: 12.0, + ), + ), + ), + Expanded( + child: Text( + "passwordHint".i18n, + maxLines: 1, + textAlign: TextAlign.right, + style: TextStyle( + color: + AppColors.of(context).loginSecondary, + fontWeight: FontWeight.w500, + fontSize: 12.0, + ), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: LoginInput( + style: LoginInputStyle.password, + controller: passwordController, + ), + ), + + // school + Padding( + padding: const EdgeInsets.only(bottom: 6.0), + child: Text( + "school".i18n, + maxLines: 1, + style: TextStyle( + color: AppColors.of(context).loginPrimary, + fontWeight: FontWeight.w500, + fontSize: 12.0, + ), + ), + ), + SchoolInput( + scroll: _scrollController, + controller: schoolController, + ), + ], + ), + ), + ), + + // login button + Padding( + padding: const EdgeInsets.only( + top: 35.0, + left: 22.0, + right: 22.0, + ), + child: Visibility( + visible: _loginState != LoginState.inProgress, + replacement: const Padding( + padding: EdgeInsets.symmetric(vertical: 6.0), + child: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation(Colors.white), + ), + ), + child: LoginButton( + child: Text("login".i18n, + maxLines: 1, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20.0, + )), + onPressed: () => _loginAPI(context: context), + ), + ), + ), + // error messages if (_loginState == LoginState.missingFields || _loginState == LoginState.invalidGrant || @@ -410,6 +413,7 @@ class LoginScreenState extends State { } // new login api + // ignore: non_constant_identifier_names, unused_element void _NewLoginAPI({required BuildContext context}) { String code = codeController.text; diff --git a/refilc_mobile_ui/lib/screens/settings/settings_screen.dart b/refilc_mobile_ui/lib/screens/settings/settings_screen.dart index 58bdb64..cb6b2bb 100644 --- a/refilc_mobile_ui/lib/screens/settings/settings_screen.dart +++ b/refilc_mobile_ui/lib/screens/settings/settings_screen.dart @@ -30,6 +30,7 @@ import 'package:refilc_mobile_ui/common/profile_image/profile_image.dart'; import 'package:refilc_mobile_ui/common/soon_alert/soon_alert.dart'; // import 'package:refilc_mobile_ui/common/soon_alert/soon_alert.dart'; import 'package:refilc_mobile_ui/common/splitted_panel/splitted_panel.dart'; +import 'package:refilc_mobile_ui/common/system_chrome.dart'; // import 'package:refilc_mobile_ui/common/system_chrome.dart'; import 'package:refilc_mobile_ui/common/widgets/update/updates_view.dart'; import 'package:refilc_mobile_ui/screens/news/news_screen.dart'; @@ -105,9 +106,11 @@ class SettingsScreenState extends State Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), - Provider.of(context, listen: false).refreshLogin(), ]); + Future refresh() => + Provider.of(context, listen: false).refreshLogin(); + void buildAccountTiles() { accountTiles = []; user.getUsers().forEach((account) { @@ -143,8 +146,58 @@ class SettingsScreenState extends State //? ColorUtils.stringToColor(account.name) //: Theme.of(context).colorScheme.secondary, ), - onTap: () { + onTap: () async { user.setUser(account.id); + + // check if refresh token is still valid + String? err = await refresh(); + if (err != null) { + showDialog( + context: context, + builder: (_) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0)), + title: Text('oopsie'.i18n), + content: Text('session_expired'.i18n), + actions: [ + ActionButton( + label: "Ok", + onTap: () async { + String? userId = user.id; + if (userId == null) return; + + // delete user + user.removeUser(userId); + await Provider.of(context, + listen: false) + .store + .removeUser(userId); + + // if no users, show login, else login with back button + if (user.getUsers().isNotEmpty) { + user.setUser(user.getUsers().first.id); + restore().then( + (_) => user.setUser(user.getUsers().first.id)); + + Navigator.of(context).pop(); + Navigator.of(context) + .pushNamed("login_back") + .then((value) { + setSystemChrome(context); + }); + } else { + Navigator.of(context).pop(); + Navigator.of(context) + .pushNamedAndRemoveUntil("login", (_) => false); + } + }) + ], + ), + ); + return; + } + + // switch user restore().then((_) => user.setUser(account.id)); Navigator.of(context).pop(); },