From 80259a814458258338a099e0ff28e501805545a1 Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Wed, 13 May 2020 16:52:39 +0200 Subject: [PATCH] mobile: Use new account API --- mobile/lib/screens/account_settings.dart | 261 +++++++++++++++++------ mobile/lib/screens/email.dart | 102 +++++++++ mobile/lib/screens/password.dart | 116 ++++++++++ mobile/lib/screens/register.dart | 9 +- mobile/lib/screens/settings.dart | 8 +- 5 files changed, 425 insertions(+), 71 deletions(-) create mode 100644 mobile/lib/screens/email.dart create mode 100644 mobile/lib/screens/password.dart diff --git a/mobile/lib/screens/account_settings.dart b/mobile/lib/screens/account_settings.dart index 3815171..a51e4c7 100644 --- a/mobile/lib/screens/account_settings.dart +++ b/mobile/lib/screens/account_settings.dart @@ -1,103 +1,238 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:musicus_client/musicus_client.dart'; import 'package:musicus_common/musicus_common.dart'; +import 'email.dart'; +import 'password.dart'; import 'register.dart'; -/// A screen for logging in using a Musicus account. class AccountSettingsScreen extends StatefulWidget { @override _AccountSettingsScreenState createState() => _AccountSettingsScreenState(); } class _AccountSettingsScreenState extends State { - final nameController = TextEditingController(); - final passwordController = TextEditingController(); + final _usernameController = TextEditingController(); + final _passwordController = TextEditingController(); - MusicusBackendState backend; - StreamSubscription accountSubscription; + MusicusBackendState _backend; + StreamSubscription _accountSubscription; + bool _loading = false; + bool _loggedIn = false; + String _username; + String _email; @override void didChangeDependencies() { super.didChangeDependencies(); - backend = MusicusBackend.of(context); + _backend = MusicusBackend.of(context); - if (accountSubscription != null) { - accountSubscription.cancel(); + final credentials = _backend.settings.account.value; + if (credentials != null) { + _setCredentials(credentials); + _getDetails(); } - _settingsChanged(backend.settings.account.value); - accountSubscription = backend.settings.account.listen((settings) { - _settingsChanged(settings); + _accountSubscription = _backend.settings.account.listen((credentials) { + _setCredentials(credentials); }); } - void _settingsChanged(MusicusAccountSettings settings) { - nameController.text = settings?.username ?? ''; - passwordController.text = settings?.password ?? ''; + Future _setCredentials(MusicusAccountCredentials credentials) async { + if (credentials != null) { + if (mounted) { + setState(() { + _loggedIn = true; + _username = credentials.username; + }); + } + } + } + + Future _getDetails() async { + setState(() { + _email = null; + }); + + final email = (await _backend.client.getAccountDetails()).email; + + if (mounted) { + setState(() { + _email = email; + }); + } } @override Widget build(BuildContext context) { + List children; + + if (_loggedIn) { + children = [ + Material( + elevation: 2.0, + child: ListTile( + title: Text('Logged in as: $_username'), + ), + ), + ListTile( + title: Text('E-mail address'), + subtitle: Text( + _email != null ? _email.isNotEmpty ? _email : 'Not set' : '...'), + trailing: const Icon(Icons.chevron_right), + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => EmailScreen( + email: _email, + ), + ), + ); + + _getDetails(); + }, + ), + ListTile( + title: Text('Change password'), + trailing: const Icon(Icons.chevron_right), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PasswordScreen(), + ), + ); + }, + ), + ListTile( + title: Text('Logout'), + onTap: () async { + await _backend.settings.clearAccount(); + Navigator.pop(context); + }, + ), + ]; + } else { + children = [ + Padding( + padding: const EdgeInsets.only( + left: 16.0, + right: 16.0, + top: 16.0, + bottom: 8.0, + ), + child: Text( + 'Enter your Musicus account credentials:', + style: Theme.of(context).textTheme.subtitle1, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: TextField( + controller: _usernameController, + decoration: InputDecoration( + labelText: 'User name', + ), + ), + ), + SizedBox( + height: 16.0, + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: TextField( + controller: _passwordController, + obscureText: true, + decoration: InputDecoration( + labelText: 'Password', + ), + ), + ), + SizedBox( + height: 32.0, + ), + ListTile( + title: Text('Create a new account'), + onTap: () { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => RegisterScreen(), + ), + ); + }, + ), + ]; + } + return Scaffold( appBar: AppBar( - title: Text('Account settings'), + title: Text('Musicus account'), actions: [ - FlatButton( - onPressed: () async { - await backend.settings.setAccount(MusicusAccountSettings( - username: nameController.text, - password: passwordController.text, - )); + Builder( + builder: (context) { + if (_loggedIn) { + return Container(); + } else if (_loading) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: SizedBox( + width: 24.0, + height: 24.0, + child: CircularProgressIndicator( + strokeWidth: 2.0, + ), + ), + ), + ); + } else { + return FlatButton( + onPressed: () async { + setState(() { + _loading = true; + }); - Navigator.pop(context); + final credentials = MusicusAccountCredentials( + username: _usernameController.text, + password: _passwordController.text, + ); + + _backend.client.credentials = credentials; + + try { + await _backend.client.login(); + await _backend.settings.setAccount(credentials); + Navigator.pop(context); + } on MusicusLoginFailedException { + Scaffold.of(context).showSnackBar( + SnackBar( + content: Text('Login failed'), + ), + ); + } + + setState(() { + _loading = false; + }); + }, + child: Text('LOGIN'), + ); + } }, - child: Text('LOGIN'), ), ], ), body: ListView( - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: TextField( - controller: nameController, - decoration: InputDecoration( - labelText: 'User name', - ), - ), - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: TextField( - controller: passwordController, - obscureText: true, - decoration: InputDecoration( - labelText: 'Password', - ), - ), - ), - ListTile( - title: Text('Create a new account'), - onTap: () { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => RegisterScreen(), - ), - ); - }, - ), - ListTile( - title: Text('Don\'t use an account'), - onTap: () { - backend.settings.clearAccount(); - Navigator.pop(context); - }, - ), - ], + children: children, ), ); } @@ -105,6 +240,6 @@ class _AccountSettingsScreenState extends State { @override void dispose() { super.dispose(); - accountSubscription.cancel(); + _accountSubscription.cancel(); } } diff --git a/mobile/lib/screens/email.dart b/mobile/lib/screens/email.dart new file mode 100644 index 0000000..eedcf3b --- /dev/null +++ b/mobile/lib/screens/email.dart @@ -0,0 +1,102 @@ +import 'package:flutter/material.dart'; +import 'package:musicus_common/musicus_common.dart'; + +class EmailScreen extends StatefulWidget { + final String email; + + EmailScreen({ + this.email, + }); + + @override + _EmailScreenState createState() => _EmailScreenState(); +} + +class _EmailScreenState extends State { + final _emailController = TextEditingController(); + + bool _loading = false; + + @override + void initState() { + super.initState(); + + if (widget.email != null) { + _emailController.text = widget.email; + } + } + + Future _setEmail(String email) async { + setState(() { + _loading = true; + }); + + final backend = MusicusBackend.of(context); + + await backend.client.updateAccount( + newEmail: email, + ); + + setState(() { + _loading = false; + }); + + Navigator.pop(context); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('E-mail address'), + actions: [ + Builder( + builder: (context) { + if (_loading) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: SizedBox( + width: 24.0, + height: 24.0, + child: CircularProgressIndicator( + strokeWidth: 2.0, + ), + ), + ), + ); + } else { + return FlatButton( + onPressed: () { + _setEmail(_emailController.text); + }, + child: Text('DONE'), + ); + } + }, + ), + ], + ), + body: ListView( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + controller: _emailController, + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + labelText: 'E-mail', + ), + ), + ), + ListTile( + title: Text('Delete E-mail address'), + onTap: () { + _setEmail(''); + }, + ), + ], + ), + ); + } +} diff --git a/mobile/lib/screens/password.dart b/mobile/lib/screens/password.dart new file mode 100644 index 0000000..38b2417 --- /dev/null +++ b/mobile/lib/screens/password.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:musicus_client/musicus_client.dart'; +import 'package:musicus_common/musicus_common.dart'; + +class PasswordScreen extends StatefulWidget { + @override + _PasswordScreenState createState() => _PasswordScreenState(); +} + +class _PasswordScreenState extends State { + final _oldPasswordController = TextEditingController(); + final _newPasswordController = TextEditingController(); + final _repeatController = TextEditingController(); + + bool _loading = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Change password'), + actions: [ + Builder( + builder: (context) { + if (_loading) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: SizedBox( + width: 24.0, + height: 24.0, + child: CircularProgressIndicator( + strokeWidth: 2.0, + ), + ), + ), + ); + } else { + return FlatButton( + onPressed: () async { + final backend = MusicusBackend.of(context); + final password = _newPasswordController.text; + + if (_oldPasswordController.text == + backend.settings.account.value.password && + password.isNotEmpty && + password == _repeatController.text) { + setState(() { + _loading = true; + }); + + await backend.client.updateAccount( + newPassword: password, + ); + + await backend.settings + .setAccount(MusicusAccountCredentials( + username: backend.settings.account.value.username, + password: password, + )); + + setState(() { + _loading = false; + }); + + Navigator.pop(context); + } else { + Scaffold.of(context).showSnackBar(SnackBar( + content: Text('Invalid inputs'), + )); + } + }, + child: Text('DONE'), + ); + } + }, + ), + ], + ), + body: ListView( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + controller: _oldPasswordController, + obscureText: true, + decoration: InputDecoration( + labelText: 'Old password', + ), + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + controller: _newPasswordController, + obscureText: true, + decoration: InputDecoration( + labelText: 'New password', + ), + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + controller: _repeatController, + obscureText: true, + decoration: InputDecoration( + labelText: 'New password (repeat)', + ), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/screens/register.dart b/mobile/lib/screens/register.dart index 056465d..3944afe 100644 --- a/mobile/lib/screens/register.dart +++ b/mobile/lib/screens/register.dart @@ -34,11 +34,11 @@ class _RegisterScreenState extends State { _loading = true; }); - final success = await backend.client.register(User( - name: nameController.text, + final success = await backend.client.registerAccount( + username: nameController.text, email: emailController.text, password: passwordController.text, - )); + ); setState(() { _loading = false; @@ -46,9 +46,8 @@ class _RegisterScreenState extends State { if (success) { await backend.settings - .setAccount(MusicusAccountSettings( + .setAccount(MusicusAccountCredentials( username: nameController.text, - email: emailController.text, password: passwordController.text, )); diff --git a/mobile/lib/screens/settings.dart b/mobile/lib/screens/settings.dart index 227c5ef..6c9ea9d 100644 --- a/mobile/lib/screens/settings.dart +++ b/mobile/lib/screens/settings.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:musicus_client/musicus_client.dart'; import 'package:musicus_common/musicus_common.dart'; import 'account_settings.dart'; @@ -61,14 +62,15 @@ class SettingsScreen extends StatelessWidget { ); }, ), - StreamBuilder( + StreamBuilder( stream: settings.account, builder: (context, snapshot) { - final a = snapshot.data; + final credentials = snapshot.data; return ListTile( title: Text('Account settings'), - subtitle: Text(a != null ? a.username : 'No account'), + subtitle: Text( + credentials != null ? credentials.username : 'No account'), trailing: const Icon(Icons.chevron_right), onTap: () { Navigator.push(