import 'dart:convert'; import 'package:auto_route/auto_route.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:telnow_mobile_new/src/api/api_auth_provider.dart'; import 'package:telnow_mobile_new/src/api/api_auth_repository.dart'; import 'package:telnow_mobile_new/src/api/jwt_token.dart'; import 'package:telnow_mobile_new/src/injector/injector.dart'; import 'package:telnow_mobile_new/src/layouts/components/template.dart'; import 'package:telnow_mobile_new/src/model/login/login_body.dart'; import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart'; import 'package:telnow_mobile_new/src/utils/U.dart'; import 'package:telnow_mobile_new/src/utils/cache_manager.dart'; import 'package:toggle_switch/toggle_switch.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:telnow_mobile_new/src/model/token/token.dart'; @RoutePage() class LoginPage extends StatefulWidget { const LoginPage({Key? key}) : super(key: key); @override State createState() => _LoginPageState(); } class _LoginPageState extends State { final JwtToken token = JwtToken(); final ApiAuthProvider apiAuthProvider = ApiAuthProvider(); final ApiAuthRepository apiAuthRepository = ApiAuthRepository(); final SharedPreferencesManager sharedPreferencesManager = locator(); String companyName = ''; String serialNumber = ''; String companyLogo = ''; bool loading = false; String username = ''; String password = ''; String imageUrl = ''; List lang = []; @override void initState() { try{imageUrl = apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';}catch(e){} getCompanyName(); // TODO: implement initState super.initState(); } getCompanyName() async { var lics = await U.reloadLicense(); if (lics != null && mounted) { setState(() { companyName = lics['companyName']??''; serialNumber = lics['serialNumber']??''; companyLogo = lics['logo']??''; if(1698720817 <= lics['serverVersion']){ lang = lics['languages'] != null ? lics['languages'].split(',') : []; } }); } } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, body: Stack( children: [ Container( width: double.infinity, height: double.infinity, child: Image.network(imageUrl, fit: BoxFit.cover, width: double.infinity, height: double.infinity, errorBuilder: (context, error, stackTrace) { return Image.asset('assets/image/background/background.jpg', fit: BoxFit.cover, width: double.infinity, height: double.infinity); }), ), SingleChildScrollView( child: Container( padding: EdgeInsets.fromLTRB(16, kIsWeb?80:50, 16, 16), width: MediaQuery.of(context).size.width, height: kIsWeb && MediaQuery.of(context).size.height<700 ? 700 : MediaQuery.of(context).size.height, decoration: BoxDecoration(gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [primaryColor.withValues(alpha: 0.50), Colors.black.withValues(alpha: 0.91)])), child: Column( children: [ kIsWeb?Container():Container( margin: EdgeInsets.only(bottom: companyLogo.isNotEmpty?50:120), child: GestureDetector( child: Row( children: [ U.iconsax('arrow-left', color: Colors.white), SizedBox(width: 15), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('buttonBack'.tr(), style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500)), Text('buttonScan'.tr(), style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500)), ], ) ], ), onTap: (){ context.router.removeLast(); context.navigateToPath('/qr?new=true'); }, ), ), Image.asset('assets/image/logo/logo.png', height: companyLogo.isNotEmpty?25:40), companyLogo.isNotEmpty?Container( margin: EdgeInsets.only(top: 30, bottom: 40), width: 160, height: 160, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(80)), image: DecorationImage(image: CachedNetworkImageProvider(companyLogo+'?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(companyLogo))), fit: BoxFit.cover) ), ):SizedBox(height: 115), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( alignment: Alignment.topCenter, width: U.bodyWidth(context), child: Column( children: [ loginFieldTemplate(placeholder: 'userId'.tr(), value: username, action: (val)=>username = val, submit: (val)=>loginAction()), SizedBox(height: 12), loginFieldTemplate(placeholder: 'password'.tr(), value: password, action: (val)=>password = val, isPassword: true, submit: (val)=>loginAction()), GestureDetector( child: Container( width: double.infinity, margin: EdgeInsets.only(top: 40, bottom: 20), padding: EdgeInsets.symmetric(vertical: 16), child: !loading?Text('buttonLogin'.tr(), style: TextStyle(color: Colors.white, fontSize: 17), textAlign: TextAlign.center):Center( child: SizedBox( width: 22, height: 22, child: CircularProgressIndicator(color: Colors.white), ), ), decoration: BoxDecoration( color: primaryColor.withValues(alpha: 0.50), border: Border.all(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(12)) ), ), onTap: ()=>loginAction(), ), ], ), ), companyName.isNotEmpty?Text(companyName, style: TextStyle(fontSize: 16, color: Colors.white), textAlign: TextAlign.center):Container(), SizedBox(height: companyName.isNotEmpty?5:0), serialNumber.isNotEmpty?Text('serialNumber'.tr() + ' ' + serialNumber, style: TextStyle(color: Colors.white, fontSize: 14)):Container(), ], ), ), Container( margin: EdgeInsets.only(top: 20, bottom: 5), alignment: Alignment.bottomCenter, child: GestureDetector( child: Text('policy'.tr(), style: TextStyle(color: Colors.white, fontSize: 14, decoration: TextDecoration.underline)), onTap: () async { Uri _url = Uri.parse('https://telmessenger.com/privacy-police/telnow.html'); await canLaunchUrl(_url) ? await launchUrl(_url) : print('Could not launch $_url'); }, ), ), Text('${'version'.tr()} ${ApiAuthProvider().displayVersion}', style: TextStyle(color: Colors.white, fontSize: 14)), SizedBox(height: 5), Text('Build ${ApiAuthProvider().buildNumber}', style: TextStyle(color: Colors.white, fontSize: 13)), ], ), ), ), Container( margin: EdgeInsets.only(top: kIsWeb?16:50, right: 16), child: Align( alignment: Alignment.topRight, child: lang.length > 0 ? GestureDetector( child: Container( width: 65, height: 30, padding: EdgeInsets.symmetric(horizontal: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ U.iconsax('bold/global', color: Colors.white), Text(context.locale.toString().toUpperCase(), style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold)) ], ), decoration: BoxDecoration( color: Colors.deepOrange, borderRadius: BorderRadius.all(Radius.circular(50)) ), ), onTap: () => changeLangLogin(context, lang), ) : ToggleSwitch( minWidth: 40, minHeight: 30, cornerRadius: 20, initialLabelIndex: context.locale.toString() == 'en' ? 0 : 1, activeBgColor: [Colors.deepOrange], activeFgColor: Colors.white, inactiveBgColor: Colors.white54, inactiveFgColor: Colors.white, labels: ['EN', 'ID'], onToggle: (index) { if (index == 0) { context.setLocale(Locale('en')); } else { context.setLocale(Locale('id')); } }, ), ), ) ], ), ); } loginAction() async{ if(!loading){ setState(()=>loading = true); String errMsg = ''; if (password.isEmpty) { errMsg = 'passwordEmpty'.tr(); } else if (username.isEmpty) { errMsg = 'userEmpty'.tr(); } if (errMsg.isNotEmpty) { showError(context, errMsg); setState(()=>loading = false); return; } Token token = await apiAuthRepository.postLoginUser(LoginBody(username.trim(), password.trim(), "password")); if (token.error != null) { showError(context, token.error); setState(()=>loading = false); return; } var pid = U.getPidFromUrl(context.router.currentUrl); await sharedPreferencesManager.putString(SharedPreferencesManager.keyAccessToken, token.accessToken!); await sharedPreferencesManager.putString(SharedPreferencesManager.keyRefreshToken, token.refreshToken!); if(await setUsername()){ var data = { 'username': username.trim(), 'password': password.trim() }; Navigator.push(context, MaterialPageRoute(builder: (context) => ForceChgPassPage(data: data))).then((value) async { if (!U.getChangedPassword()) { await U.clearPreferences(); U.setHidePayload(true); context.router.removeLast(); context.navigateToPath("/app/$pid/menu"); return; } }); } else{ await sharedPreferencesManager.putBool(SharedPreferencesManager.keyIsLogin, true); await sharedPreferencesManager.putString(SharedPreferencesManager.keyScoope, 'INSIDE'); await sharedPreferencesManager.putInt(SharedPreferencesManager.keyCountRefreshToken, 0); await sharedPreferencesManager.putString(SharedPreferencesManager.keyPendingData, jsonEncode([])); U.setHidePayload(true); context.router.removeLast(); context.navigateToPath("/app/$pid/menu"); } } } setUsername() async { try{ String? accessToken = sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessToken); var jwt = token.parseJwtPayLoad(accessToken!); await sharedPreferencesManager.putString(SharedPreferencesManager.keyUsername, jwt['user_name'].substring(4)); await U.setChangedPassword(false); return jwt['forceChgPass']; } catch(e) { print(e.toString()); } } } changeLangLogin(BuildContext context, List lang){ showModalBottomSheet( context: context, backgroundColor: Colors.white, builder: (BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: EdgeInsets.symmetric(vertical: 16), child: Center( child: Text('chooseLanguage'.tr(), style: TextStyle(color: textColor)), ), ), divider(), SizedBox(height: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ListTile( title: Text('bahasa'.tr(), style: TextStyle(color: textColor, fontSize: 14)), trailing: Icon(context.locale.toString()=='id'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18), onTap: context.locale.toString() != 'id' ? () async { Navigator.of(context).pop(); context.setLocale(Locale('id')); } : null, ), ListTile( title: Text('english'.tr(), style: TextStyle(color: textColor, fontSize: 14)), trailing: Icon(context.locale.toString()=='en'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18), onTap: context.locale.toString() != 'en' ? () async { Navigator.of(context).pop(); context.setLocale(Locale('en')); } : null, ), lang.contains('ja') ? ListTile( title: Text('japanese'.tr(), style: TextStyle(color: textColor, fontSize: 14)), trailing: Icon(context.locale.toString()=='ja'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18), onTap: context.locale.toString() != 'ja' ? () async { Navigator.of(context).pop(); context.setLocale(Locale('ja')); } : null, ) : Container(), lang.contains('zh') ? ListTile( title: Text('chinese'.tr(), style: TextStyle(color: textColor, fontSize: 14)), trailing: Icon(context.locale.toString()=='zh'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18), onTap: context.locale.toString() != 'zh' ? () async { Navigator.of(context).pop(); context.setLocale(Locale('zh')); } : null, ) : Container(), lang.contains('ko') ? ListTile( title: Text('korean'.tr(), style: TextStyle(color: textColor, fontSize: 14)), trailing: Icon(context.locale.toString()=='ko'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18), onTap: context.locale.toString() != 'ko' ? () async { Navigator.of(context).pop(); context.setLocale(Locale('ko')); } : null, ) : Container() ], ), SizedBox(height: 16) ], ); } ); } //------------------------------------------------------------------------------ class ForceChgPassPage extends StatefulWidget { Map data; ForceChgPassPage({required this.data, Key? key}) : super(key: key); @override State createState() => _ForceChgPassPageState(); } class _ForceChgPassPageState extends State { final JwtToken token = JwtToken(); final ApiAuthProvider apiAuthProvider = ApiAuthProvider(); final ApiAuthRepository apiAuthRepository = ApiAuthRepository(); final SharedPreferencesManager sharedPreferencesManager = locator(); var tokenData; String companyName = ''; String serialNumber = ''; String guestName = ''; bool loading = false; String imageUrl = ''; String newPassword = ''; String cfrmPassword = ''; @override void initState() { try{imageUrl = apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';}catch(e){} getTokenData(); getCompanyName(); // TODO: implement initState super.initState(); } getTokenData() async { String accessToken = sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessToken)!; var jwt = token.parseJwtPayLoad(accessToken); var user = await apiAuthProvider.getData('/api/informants/'+jwt['userId'].toString(), null, context); if(user!=null){ setState((){ tokenData = jwt; context.setLocale(Locale(tokenData['language'].toLowerCase())); guestName = user['guestName']; }); } } getCompanyName() async { var lics = await U.reloadLicense(); if (lics != null && mounted) { setState(() { companyName = lics['companyName']??''; serialNumber = lics['serialNumber']??''; }); } } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: (){ sharedPreferencesManager.clearKey(SharedPreferencesManager.keyAccessToken); sharedPreferencesManager.clearKey(SharedPreferencesManager.keyRefreshToken); sharedPreferencesManager.clearKey(SharedPreferencesManager.keyUsername); sharedPreferencesManager.clearKey(SharedPreferencesManager.keyIsLogin); sharedPreferencesManager.clearKey(SharedPreferencesManager.keyScoope); sharedPreferencesManager.clearKey(SharedPreferencesManager.keyMenuDisplay); sharedPreferencesManager.clearKey(SharedPreferencesManager.keyAutoTranslate); sharedPreferencesManager.clearKey(SharedPreferencesManager.keyPendingData); navigateBack(context); return Future.value(true); }, child: Scaffold( resizeToAvoidBottomInset: false, body: Stack( children: [ SizedBox( width: double.infinity, height: double.infinity, child: Image.network(imageUrl, fit: BoxFit.cover, width: double.infinity, height: double.infinity, errorBuilder: (context, error, stackTrace) { return Image.asset('assets/image/background/background.jpg', fit: BoxFit.cover, width: double.infinity, height: double.infinity); }), ), Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ primaryColor.withValues(alpha: 0.50), Colors.black.withValues(alpha: 0.91) ] ) ), child: SingleChildScrollView( padding: EdgeInsets.fromLTRB(16, 50, 16, 16), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( margin: EdgeInsets.only(bottom: 100), child: Row( children: [ GestureDetector( child: U.iconsax('arrow-left', color: Colors.white), onTap: (){ context.router.removeLast(); context.navigateToPath('/qr?new=true'); }, ), SizedBox(width: 15), Expanded( child: GestureDetector( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('buttonBack'.tr(), style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500)), Text('buttonScan'.tr(), style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500)), ], ), onTap: (){ context.router.removeLast(); context.navigateToPath('/qr?new=true'); }, ), ), Image.asset('assets/image/logo/logo.png', width: 120) ], ) ), Column( children: [ Text('${'welcome'.tr()} $guestName', style: TextStyle(color: Colors.white, fontSize: 16), textAlign: TextAlign.center), SizedBox(height: 16), Text('createNewPassText'.tr(), style: TextStyle(color: Colors.white, fontSize: 16), textAlign: TextAlign.center), ], ), SizedBox(height: 80), Container( alignment: Alignment.topCenter, width: U.bodyWidth(context), child: Column( children: [ loginFieldTemplate(placeholder: 'newPassword'.tr(), value: newPassword, action: (val)=>newPassword = val, isPassword: true, submit: (val)=>loginAction()), SizedBox(height: 12), loginFieldTemplate(placeholder: 'confirmPassword'.tr(), value: cfrmPassword, action: (val)=>cfrmPassword = val, isPassword: true, submit: (val)=>loginAction()), GestureDetector( child: Container( width: double.infinity, margin: EdgeInsets.only(top: 40, bottom: 20), padding: EdgeInsets.symmetric(vertical: 16), decoration: BoxDecoration( color: primaryColor.withValues(alpha: 0.50), border: Border.all(color: Colors.white), borderRadius: BorderRadius.all(Radius.circular(12)) ), child: !loading?Text('buttonLogin'.tr(), style: TextStyle(color: Colors.white, fontSize: 17), textAlign: TextAlign.center):Center( child: SizedBox( width: 22, height: 22, child: CircularProgressIndicator(color: Colors.white), ), ), ), onTap: ()=>loginAction(), ), ], ), ), companyName.isNotEmpty?Text(companyName, style: TextStyle(fontSize: 16, color: Colors.white), textAlign: TextAlign.center):Container(), SizedBox(height: companyName.isNotEmpty?5:0), serialNumber.isNotEmpty?Text('${'serialNumber'.tr()} $serialNumber', style: TextStyle(color: Colors.white, fontSize: 14)):Container(), Container( margin: const EdgeInsets.only(top: 145), alignment: Alignment.bottomCenter, child: Text('version'.tr() + ' ' + ApiAuthProvider().displayVersion, style: TextStyle(color: Colors.white, fontSize: 14)) ), ], ), ), ), ], ), ), ); } loginAction() async{ if(!loading){ if(newPassword.isNotEmpty && cfrmPassword.isNotEmpty){ if(newPassword.trim() == cfrmPassword.trim()){ setState(()=>loading = true); var data = {'oldPassword': widget.data['password'], 'password': newPassword.trim()}; try { var res = await apiAuthProvider.patchData('/api/informants/${tokenData['userId']}/changePassword', data, context); if (res != null) { String password = tokenData['related'] && tokenData['tenantCode'] != null && tokenData['tenantCode'] != '' ? tokenData['tenantCode'] + ' ' + data['password'] : data['password']; LoginBody loginBody = LoginBody(widget.data['username']!, password, 'password'); Token token = await apiAuthRepository.postLoginUser(loginBody); if (token.error != null) { setState(()=>loading = false); showError(context, token.error); } else { await U.setChangedPassword(true); var parsedToken = U.token.parseJwtPayLoad(token.accessToken!); await sharedPreferencesManager.putString(SharedPreferencesManager.keyAccessToken, token.accessToken!); await sharedPreferencesManager.putString(SharedPreferencesManager.keyRefreshToken, token.refreshToken!); await sharedPreferencesManager.putString(SharedPreferencesManager.keyUsername, tokenData['related'] ? parsedToken['user_name'].split("-").last : widget.data['username']); await sharedPreferencesManager.putBool(SharedPreferencesManager.keyIsLogin, true); await sharedPreferencesManager.putString(SharedPreferencesManager.keyScoope, 'INSIDE'); await sharedPreferencesManager.putInt(SharedPreferencesManager.keyCountRefreshToken, 0); await sharedPreferencesManager.putString(SharedPreferencesManager.keyPendingData, jsonEncode([])); U.setHidePayload(true); // var pid = U.getAccessCode(); var pid = U.getPidFromUrl(context.router.currentUrl); context.router.removeLast(); context.navigateToPath("/app/$pid/menu"); } } } catch(e) { print(e.toString()); setState(()=>loading = false); } } else{ showError(context, 'checkNewPass'.tr()); } } else{ showError(context, 'cannotEmpty'.tr()); } } } }