From abf0196de31648b2db6383b10ab57831c7689140 Mon Sep 17 00:00:00 2001 From: konrad Date: Mon, 8 Oct 2018 14:26:01 +0000 Subject: [PATCH] Added register (#13) --- lib/api/user_implementation.dart | 10 ++ lib/global.dart | 3 +- lib/pages/login_page.dart | 24 +++-- lib/pages/register_page.dart | 152 +++++++++++++++++++++++++++++++ lib/service/mocked_services.dart | 5 + lib/service/services.dart | 1 + lib/utils/validator.dart | 13 +++ 7 files changed, 199 insertions(+), 9 deletions(-) create mode 100644 lib/pages/register_page.dart create mode 100644 lib/utils/validator.dart diff --git a/lib/api/user_implementation.dart b/lib/api/user_implementation.dart index 61f3a99..a07bb2e 100644 --- a/lib/api/user_implementation.dart +++ b/lib/api/user_implementation.dart @@ -19,6 +19,16 @@ class UserAPIService extends APIService implements UserService { .then((user) => UserTokenPair(user, token)); } + @override + Future register(String username, email, password) async { + var newUser = await client.post('/register', body: { + 'username': username, + 'email': email, + 'password': password + }).then((resp) => resp['username']); + return login(newUser, password); + } + @override Future getCurrentUser() { return client.get('/user').then((map) => User.fromJson(map)); diff --git a/lib/global.dart b/lib/global.dart index f75de00..622ef19 100644 --- a/lib/global.dart +++ b/lib/global.dart @@ -37,8 +37,7 @@ class VikunjaGlobalState extends State { Client get client => _client; UserManager get userManager => new UserManager(_storage); - UserService get userService => new UserAPIService(_client); - UserService newLoginService(base) => new UserAPIService(Client(null, base)); + UserService newUserService(base) => new UserAPIService(Client(null, base)); NamespaceService get namespaceService => new NamespaceAPIService(client); TaskService get taskService => new TaskAPIService(client); ListService get listService => new ListAPIService(client); diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index 17bbd33..eb65be0 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -1,15 +1,13 @@ import 'package:flutter/material.dart'; import 'package:vikunja_app/global.dart'; -import 'package:vikunja_app/main.dart'; +import 'package:vikunja_app/pages/register_page.dart'; +import 'package:vikunja_app/utils/validator.dart'; class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); } -final RegExp _url = new RegExp( - r'https?:\/\/((([a-zA-Z0-9.\-\_]+)\.[a-zA-Z]+)|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))(:[0-9]+)?'); - class _LoginPageState extends State { final _formKey = GlobalKey(); String _server, _username, _password; @@ -44,8 +42,7 @@ class _LoginPageState extends State { child: TextFormField( onSaved: (serverAddress) => _server = serverAddress, validator: (address) { - var hasMatch = _url.hasMatch(address); - return hasMatch ? null : 'Invalid URL'; + return isUrl(address) ? null : 'Invalid URL'; }, decoration: new InputDecoration( labelText: 'Server Address'), @@ -85,6 +82,19 @@ class _LoginPageState extends State { ? CircularProgressIndicator() : Text('Login'), ))), + Builder( + builder: (context) => ButtonTheme( + height: _loading ? 55.0 : 36.0, + child: RaisedButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + RegisterPage())), + child: _loading + ? CircularProgressIndicator() + : Text('Register'), + ))), ], )), ), @@ -96,7 +106,7 @@ class _LoginPageState extends State { try { var vGlobal = VikunjaGlobal.of(context); var newUser = - await vGlobal.newLoginService(_server).login(_username, _password); + await vGlobal.newUserService(_server).login(_username, _password); vGlobal.changeUser(newUser.user, token: newUser.token, base: _server); } catch (ex) { showDialog( diff --git a/lib/pages/register_page.dart b/lib/pages/register_page.dart new file mode 100644 index 0000000..8a0433a --- /dev/null +++ b/lib/pages/register_page.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; +import 'package:vikunja_app/global.dart'; +import 'package:vikunja_app/utils/validator.dart'; + +class RegisterPage extends StatefulWidget { + @override + _RegisterPageState createState() => _RegisterPageState(); +} + +class _RegisterPageState extends State { + final _formKey = GlobalKey(); + final passwordController = TextEditingController(); + String _server, _username, _email, _password; + bool _loading = false; + + @override + Widget build(BuildContext ctx) { + return Scaffold( + appBar: AppBar( + title: Text('Register to Vikunja'), + ), + body: Builder( + builder: (BuildContext context) => SafeArea( + top: false, + bottom: false, + child: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Image( + image: AssetImage('assets/vikunja_logo.png'), + height: 128.0, + semanticLabel: 'Vikunja Logo', + ), + ), + Padding( + padding: EdgeInsets.all(8.0), + child: TextFormField( + onSaved: (serverAddress) => _server = serverAddress, + validator: (address) { + return isUrl(address) ? null : 'Invalid URL'; + }, + decoration: new InputDecoration( + labelText: 'Server Address'), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + onSaved: (username) => _username = username.trim(), + validator: (username) { + return username.trim().isNotEmpty ? null : 'Please specify a username'; + }, + decoration: + new InputDecoration(labelText: 'Username'), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + onSaved: (email) => _email = email, + validator: (email) { + return isEmail(email) + ? null + : 'Email adress is invalid'; + }, + decoration: + new InputDecoration(labelText: 'Email Address'), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + controller: passwordController, + onSaved: (password) => _password = password, + validator: (password) { + return password.length >= 8 ? null : 'Please use at least 8 characters'; + }, + decoration: + new InputDecoration(labelText: 'Password'), + obscureText: true, + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextFormField( + validator: (password) { + return passwordController.text == password + ? null + : 'Passwords don\'t match.'; + }, + decoration: new InputDecoration( + labelText: 'Repeat Password'), + obscureText: true, + ), + ), + Builder( + builder: (context) => ButtonTheme( + height: _loading ? 55.0 : 36.0, + child: RaisedButton( + onPressed: !_loading + ? () { + if (_formKey.currentState + .validate()) { + Form.of(context).save(); + _registerUser(context); + } else { + print("awhat"); + } + } + : null, + child: _loading + ? CircularProgressIndicator() + : Text('Register'), + ))), + ], + )), + ), + )); + } + + _registerUser(BuildContext context) async { + setState(() => _loading = true); + try { + var vGlobal = VikunjaGlobal.of(context); + var newUserLoggedIn = await vGlobal + .newUserService(_server) + .register(_username, _email, _password); + vGlobal.changeUser(newUserLoggedIn.user, + token: newUserLoggedIn.token, base: _server); + } catch (ex) { + showDialog( + context: context, + builder: (context) => new AlertDialog( + title: const Text( + 'Registration failed! Please check your server url and credentials.'), + actions: [ + FlatButton( + onPressed: () => Navigator.pop(context), + child: const Text('CLOSE')) + ], + )); + } finally { + setState(() { + _loading = false; + }); + } + } +} diff --git a/lib/service/mocked_services.dart b/lib/service/mocked_services.dart index 01e914a..df52b6b 100644 --- a/lib/service/mocked_services.dart +++ b/lib/service/mocked_services.dart @@ -151,6 +151,11 @@ class MockedUserService implements UserService { return Future.value(UserTokenPair(_users[1], 'abcdefg')); } + @override + Future register(String username, email, password) { + return Future.value(UserTokenPair(_users[1], 'abcdefg')); + } + @override Future getCurrentUser() { return Future.value(_users[1]); diff --git a/lib/service/services.dart b/lib/service/services.dart index 60363c1..be9f7b8 100644 --- a/lib/service/services.dart +++ b/lib/service/services.dart @@ -29,5 +29,6 @@ abstract class TaskService { abstract class UserService { Future login(String username, password); + Future register(String username, email, password); Future getCurrentUser(); } diff --git a/lib/utils/validator.dart b/lib/utils/validator.dart new file mode 100644 index 0000000..a363bb6 --- /dev/null +++ b/lib/utils/validator.dart @@ -0,0 +1,13 @@ +final RegExp _emailRegex = new RegExp( + r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'); + +bool isEmail(email) { + return _emailRegex.hasMatch(email); +} + +final RegExp _url = new RegExp( + r'https?:\/\/((([a-zA-Z0-9.\-\_]+)\.[a-zA-Z]+)|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))(:[0-9]+)?'); + +bool isUrl(url) { + return _url.hasMatch(url); +}