import 'dart:ui' show ImageFilter; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:esse/l10n/localizations.dart'; import 'package:esse/widgets/button_text.dart'; import 'package:esse/widgets/shadow_dialog.dart'; import 'package:esse/widgets/show_pin.dart'; import 'package:esse/pages/account_generate.dart'; import 'package:esse/pages/account_restore.dart'; import 'package:esse/pages/account_quick.dart'; import 'package:esse/utils/logined_cache.dart'; import 'package:esse/utils/better_print.dart'; import 'package:esse/account.dart'; import 'package:esse/global.dart'; import 'package:esse/rpc.dart'; import 'package:esse/provider.dart'; import 'package:esse/apps/device/provider.dart'; import 'package:esse/apps/chat/provider.dart'; import 'package:esse/apps/group_chat/provider.dart'; class SecurityPage extends StatefulWidget { const SecurityPage({Key? key}) : super(key: key); @override _SecurityPageState createState() => _SecurityPageState(); } class _SecurityPageState extends State { Map _accounts = {}; bool _loaded = false; bool _accountsLoaded = false; String _selectedUserId = ''; String _selectedUserLock = ''; SystemUiOverlayStyle style = SystemUiOverlayStyle.dark; @override initState() { super.initState(); loadAccounts(); } @override Widget build(BuildContext context) { final color = Theme.of(context).colorScheme; final isLight = color.brightness == Brightness.light; final lang = AppLocalizations.of(context); if (isLight) { style = SystemUiOverlayStyle.dark; } else { style = SystemUiOverlayStyle.light; } double maxHeight = (MediaQuery.of(context).size.height - 400) / 2; if (maxHeight < 20.0) { maxHeight = 20.0; } return Scaffold( body: AnnotatedRegion( value: style.copyWith(statusBarColor: color.background), child: SafeArea( child: Stack(children: [ Container( padding: const EdgeInsets.all(20.0), height: MediaQuery.of(context).size.height, decoration: BoxDecoration( image: DecorationImage( image: AssetImage( isLight ? 'assets/images/background_light.jpg' : 'assets/images/background_dark.jpg' ), fit: BoxFit.cover, ), ), child: SingleChildScrollView( child: Column( children: [ SizedBox(height: maxHeight), Container( width: 120.0, height: 120.0, decoration: BoxDecoration( boxShadow: [ BoxShadow( color: Color(0xFF2B2E38).withOpacity(0.3), spreadRadius: 5.0, blurRadius: 15.0, offset: Offset(0, 10), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(15.0), child: Image(image: isLight ? AssetImage('assets/logo/logo_light.png') : AssetImage('assets/logo/logo_dark.png')) ) ), const SizedBox(height: 40.0), Text('ESSE', style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)), const SizedBox(height: 40.0), loginForm(color, lang), const SizedBox(height: 20.0), ButtonText(text: lang.ok, enable: _accountsLoaded, action: () => loginAction(lang.verifyPin)), const SizedBox(height: 20.0), InkWell( child: Container(width: 600.0, height: 50.0, decoration: BoxDecoration( border: Border.all(color: Color(0xFF6174FF)), borderRadius: BorderRadius.circular(10.0)), child: Center(child: Text(lang.loginQuick, style: TextStyle( fontSize: 20.0, color: Color(0xFF6174FF) ))), ), onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => AccountQuickPage()) ), ), Padding( padding: const EdgeInsets.only(top: 20), child: Container( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => AccountRestorePage())), child: Text( lang.loginRestore, style: TextStyle(fontSize: 16), ), ), const SizedBox(width: 10.0), Text("|", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(width: 10.0), TextButton( onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => AccountGeneratePage())), child: Text( lang.loginNew, style: TextStyle(fontSize: 16), ), ), ], ), ), ), ] ) ) ), this._loaded ? Container() : LoaderTransparent(color: color.primary) ] ) ) ) ); } void loadAccounts() async { // init rpc. if (!rpc.isLinked()) { await rpc.init(Global.wsRpc); } // init system info. rpc.send('account-system-info', []); // check if has logined. final loginedAccounts = await getLogined(); if (loginedAccounts.length != 0) { print("INFO: START LOGINED USE CACHE"); final mainAccount = loginedAccounts[0]; final res = await httpPost(Global.httpRpc, 'account-login', [mainAccount.gid, mainAccount.lock]); if (res.isOk) { Map accounts = {}; loginedAccounts.forEach((account) { accounts[account.gid] = account; }); Provider.of(context, listen: false).autoAccounts(mainAccount.gid, accounts); Provider.of(context, listen: false).updateActived(); Provider.of(context, listen: false).updateActived(); Provider.of(context, listen: false).updateActived(); Navigator.of(context).pushNamedAndRemoveUntil("/", (Route route) => false); return; } else { // TODO tostor error print(res.error); await clearLogined(); } } print("INFO: START LOGINED WITH ACCOUNTS"); final res = await httpPost(Global.httpRpc, 'account-list', []); if (res.isOk) { this._accounts.clear(); res.params.forEach((param) { this._accounts[param[0]] = Account(param[0], param[1], param[2], param[3]); }); Provider.of(context, listen: false).initAccounts(this._accounts); if (this._accounts.length > 0) { final accountId = this._accounts.keys.first; this._selectedUserId = this._accounts[accountId]!.gid; this._selectedUserLock = this._accounts[accountId]!.lock; this._accountsLoaded = true; } } else { // TODO tostor error print(res.error); } setState(() { this._loaded = true; }); } void _verifyAfter(String lock) async { final res = await httpPost(Global.httpRpc, 'account-login', [this._selectedUserId, lock]); if (res.isOk) { final mainAccount = this._accounts[this._selectedUserId]!; Provider.of(context, listen: false).updateActivedAccount(mainAccount.gid); Provider.of(context, listen: false).updateActived(); Provider.of(context, listen: false).updateActived(); Provider.of(context, listen: false).updateActived(); Navigator.of(context).pushNamedAndRemoveUntil("/", (Route route) => false); } else { // TODO tostor error print(res.error); } } void loginAction(String title) { if (this._selectedUserLock.length == 0) { _verifyAfter(''); } else { showShadowDialog( context, Icons.security_rounded, title, PinWords( hashPin: this._selectedUserLock, callback: (pinWords, lock) async { Navigator.of(context).pop(); _verifyAfter(lock); })); } } Widget loginForm(ColorScheme color, AppLocalizations lang) { return Container( width: 600.0, height: 50.0, padding: EdgeInsets.only(left: 20, right: 20), decoration: BoxDecoration( color: color.surface, borderRadius: BorderRadius.circular(15.0)), child: DropdownButtonHideUnderline( child: Theme( data: Theme.of(context).copyWith( canvasColor: color.surface, ), child: DropdownButton( hint: Text(lang.loginChooseAccount, style: TextStyle(fontSize: 16)), iconEnabledColor: Color(0xFFADB0BB), isExpanded: true, value: this._selectedUserId, onChanged: (String? gid) { if (gid != null) { setState(() { this._selectedUserId = gid; this._selectedUserLock = this._accounts[gid]!.lock; }); } }, items: this._accounts.values.map((Account account) { return DropdownMenuItem( value: account.gid, child: Row( children: [ Expanded( child: Text("${account.name}", maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 16) ), ), Text(" (${gidPrint(account.gid)})", style: TextStyle(fontSize: 16)), ] ), ); }).toList(), ), ), )); } } class LoaderTransparent extends StatelessWidget { final Color color; LoaderTransparent({required this.color}); @override Widget build(BuildContext context) { final height = MediaQuery.of(context).size.height; final width = MediaQuery.of(context).size.width; return BackdropFilter( filter: ImageFilter.blur(sigmaX: 6.0, sigmaY: 6.0), child: Container( height: height, width: width, child: Center( child: SizedBox( height: 60.0, width: 60.0, child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(this.color), strokeWidth: 12.0 ) ) ) )); } }