import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:photos/core/configuration.dart'; import 'package:photos/core/event_bus.dart'; import 'package:photos/events/user_authenticated_event.dart'; import 'package:photos/user_authenticator.dart'; class SignInWidget extends StatefulWidget { final Function() onReconfigurationRequested; const SignInWidget( this.onReconfigurationRequested, { Key key, }) : super(key: key); @override _SignInWidgetState createState() => _SignInWidgetState(); } enum Mode { sign_up, sign_in, unknown } class _SignInWidgetState extends State { Mode mode = Mode.unknown; final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); final _repeatPasswordController = TextEditingController(); @override Widget build(BuildContext context) { if (mode == Mode.sign_up) { return _getSignUpWidget(context); } else { return _getSignInWidget(context); } } Widget _getSignUpWidget(BuildContext context) { return Container( child: SingleChildScrollView( child: Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), child: Text("Create an account to get started"), ), TextFormField( decoration: InputDecoration( hintText: 'username', contentPadding: EdgeInsets.all(20), ), controller: _usernameController, autofocus: true, autocorrect: false, ), TextFormField( decoration: InputDecoration( hintText: 'password', contentPadding: EdgeInsets.all(20), ), autocorrect: false, obscureText: true, controller: _passwordController, ), TextFormField( decoration: InputDecoration( hintText: 'repeat password', contentPadding: EdgeInsets.all(20), ), autocorrect: false, obscureText: true, controller: _repeatPasswordController, ), CupertinoButton( child: Text("Sign Up"), onPressed: () async { if (_passwordController.text != _repeatPasswordController.text) { _showPasswordMismatchDialog(); } else { try { final userCreated = await UserAuthenticator.instance.create( _usernameController.text, _passwordController.text); if (userCreated) { Navigator.of(context).pop(); _showSelectEncryptionLevelDialog(); } else { _showGenericErrorDialog(); } } catch (e) { _showGenericErrorDialog(error: e); } } }, ), CupertinoButton( child: Text("Have an account?"), onPressed: () { setState(() { mode = Mode.sign_in; }); }, ), ], ), )); } Widget _getSignInWidget(BuildContext context) { return Container( child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextFormField( // initialValue: Configuration.instance.getUsername(), decoration: InputDecoration( hintText: 'username', contentPadding: EdgeInsets.all(20), ), autofocus: true, autocorrect: false, controller: _usernameController, ), TextFormField( // initialValue: Configuration.instance.getPassword(), decoration: InputDecoration( hintText: 'password', contentPadding: EdgeInsets.all(20), ), autocorrect: false, obscureText: true, controller: _passwordController, ), CupertinoButton( child: Text("Sign In"), onPressed: () async { final loggedIn = await UserAuthenticator.instance .login(_usernameController.text, _passwordController.text); if (loggedIn) { Navigator.of(context).pop(); } else { _showAuthenticationFailedErrorDialog(); } }, ), CupertinoButton( child: Text("Don't have an account?"), onPressed: () { setState(() { mode = Mode.sign_up; }); }, ), ], ), )); } void _showPasswordMismatchDialog() { showDialog( context: context, barrierDismissible: false, // user must tap button! builder: (BuildContext context) { return CupertinoAlertDialog( title: Text("Passwords don't match"), content: Padding( padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), child: Text("Please make sure that the passwords you enter match."), ), actions: [ CupertinoDialogAction( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } void _showGenericErrorDialog({Exception error}) { showDialog( context: context, barrierDismissible: false, // user must tap button! builder: (BuildContext context) { return CupertinoAlertDialog( title: Text("Ooops."), content: Padding( padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), child: error == null ? Text("Something went wrong.") : Text(error.toString()), ), actions: [ CupertinoDialogAction( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } void _showAuthenticationFailedErrorDialog() { showDialog( context: context, barrierDismissible: false, // user must tap button! builder: (BuildContext context) { return CupertinoAlertDialog( title: Text('Login failed'), content: Padding( padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), child: Text( 'Please make sure that the credentials entered are correct.'), ), actions: [ CupertinoDialogAction( child: Text('Reconfigure'), onPressed: () { Navigator.of(context).pop(); Configuration.instance.setEndpoint(null); widget.onReconfigurationRequested(); }, ), CupertinoDialogAction( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } void _showSelectEncryptionLevelDialog() { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return SelectEncryptionLevelWidget(); }, ); } } class SelectEncryptionLevelWidget extends StatelessWidget { const SelectEncryptionLevelWidget({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { return CupertinoAlertDialog( title: Text('Choose encryption level'), content: Padding( padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), child: Column( children: [ Text('Would you like to enable end-to-end encryption?'), Padding(padding: EdgeInsets.all(8)), Text( 'This will mean you will not be able to use features like search and sharing.'), ], ), ), actions: [ CupertinoDialogAction( child: Text('Use E2E encryption'), onPressed: () { Navigator.of(context).pop(); _showEnterPassphraseDialog(context); }, ), CupertinoDialogAction( child: Text("Use encryption at rest"), onPressed: () { Navigator.of(context).pop(); Bus.instance.fire(UserAuthenticatedEvent()); }, ), ], ); } void _showEnterPassphraseDialog(BuildContext context) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return PassphraseWidget(); }, ); } } class PassphraseWidget extends StatefulWidget { const PassphraseWidget({ Key key, }) : super(key: key); @override _PassphraseWidgetState createState() => _PassphraseWidgetState(); } class _PassphraseWidgetState extends State { String _passphrase = ""; @override Widget build(BuildContext context) { return CupertinoAlertDialog( title: Text('Enter passphrase'), content: Padding( padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), child: Column( children: [ Text("Do not forget this passphrase!"), Padding(padding: EdgeInsets.all(8)), CupertinoTextField( autofocus: true, style: Theme.of(context).textTheme.subtitle1, keyboardType: TextInputType.visiblePassword, onChanged: (value) { _passphrase = value; }, ), ], ), ), actions: [ CupertinoDialogAction( child: Text('Save'), onPressed: () { Navigator.of(context).pop(); Configuration.instance.generateAndSaveKey(_passphrase); Bus.instance.fire(UserAuthenticatedEvent()); }, ) ], ); } }