4
2
Fork 0

Merge branch 'feat/implementing-proper-login-register' of beep/frontend_flutter into master

pull/61/head
Sudharshan S. 2019-07-01 13:35:51 +00:00 committed by Gitea
commit 549522b95d
9 changed files with 279 additions and 57 deletions

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'src/ui/home.dart';
import "src/ui/login/welcome.dart";
import "src/services/login_manager.dart";
import 'themer.dart';
class Routes {
@ -11,13 +12,30 @@ class Routes {
};
final theme = buildTheme();
final loginManager = LoginManager();
Routes() {
runApp(MaterialApp(
title: "Beep",
theme: theme,
routes: routes,
home: Welcome(),
));
checkIfLogin();
}
checkIfLogin() async {
final authToken = await loginManager.getToken();
print(authToken);
if (authToken != "") {
runApp(MaterialApp(
title: "Beep",
theme: theme,
routes: routes,
home: Home(),
));
} else {
runApp(MaterialApp(
title: "Beep",
theme: theme,
routes: routes,
home: Welcome(),
));
}
}
}

View File

@ -12,14 +12,34 @@ class UserApiProvider {
CacheHttp cache = CacheHttp();
LoginManager loginManager = LoginManager();
Future<User> createUser(User user) async {
Future<User> createUser(
String firstName, String lastName, String phoneNumber) async {
final jwt = loginManager.getToken();
final response = await http.post("$baseUrlCore/user",
headers: {
HttpHeaders.contentTypeHeader: "application/json",
HttpHeaders.authorizationHeader: "Bearer $jwt"
},
body: user.toJson());
body: jsonEncode({
"first_name": firstName,
"last_name": lastName,
"phone_number": phoneNumber
}));
return User.fromJson(jsonDecode(response.body));
}
Future<User> registerUser(
String firstName, String lastName, String phoneNumber, String otp, String nonce) async {
final response = await http.post("$baseUrlLogin/register/$otp/$nonce",
headers: {
HttpHeaders.contentTypeHeader: "application/json",
},
body: jsonEncode({
"first_name": firstName,
"last_name": lastName,
"phone_number": phoneNumber
}));
return User.fromJson(jsonDecode(response.body));
}
@ -40,7 +60,7 @@ class UserApiProvider {
}
Future<User> fetchUserById(String id) async {
final jwt = loginManager.getToken();
final jwt = await loginManager.getToken();
try {
final responseBody =
await this.cache.fetch("$baseUrlCore/user/id/$id", headers: {
@ -52,4 +72,20 @@ class UserApiProvider {
throw e;
}
}
Future<void> updateUser(String firstName, String lastName) async {
final jwt = await loginManager.getToken();
final user = await loginManager.getUser();
final finalFirstName = firstName != "" ? firstName : user != null ? user.firstName : "";
final finalLastName = lastName != "" ? lastName : user != null ? user.lastName : "";
await http.patch("$baseUrlCore/user",
headers: {
HttpHeaders.contentTypeHeader: "application/json",
HttpHeaders.authorizationHeader: "Bearer $jwt"
},
body: jsonEncode({
"first_name": finalFirstName,
"last_name": finalLastName,
}));
}
}

View File

@ -1,5 +1,8 @@
import "dart:async";
import "dart:convert";
import "../models/user_model.dart";
import "../resources/login_api_provider.dart";
import "../resources/user_api_provider.dart";
import 'package:shared_preferences/shared_preferences.dart';
@ -15,6 +18,12 @@ class LoginManager {
return token;
}
Future<User> getUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final userString = prefs.getString("user");
return userString != null ? jsonDecode(userString) : null;
}
// Throws error status code if it occurs
Future<void> initAuthentication(String phoneNumber) async {
try {
@ -32,6 +41,19 @@ class LoginManager {
await loginApiProvider.verifyOtp(otp, this.nonce, this.clientid);
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString("token", jwt);
// Parse jwt to get userid
final parts = jwt.split('.');
if (parts.length != 3) {
throw Exception('invalid token');
}
final payload = utf8.decode(base64Url.decode(parts[1]));
final payloadMap = json.decode(payload);
final userId = payloadMap['userid'];
// Get user data
final userApiProvider = UserApiProvider();
final user =
await userApiProvider.fetchUserById(userId);
await prefs.setString("user", jsonEncode(user));
return jwt;
} catch (e) {
throw e;

View File

@ -1,9 +1,12 @@
import "package:flutter/material.dart";
import "../../services/login_manager.dart";
import "../../services/conversation_manager.dart";
import "./widgets/welcome_page.dart";
import "./widgets/login_page.dart";
import "./widgets/otp_page.dart";
import "./widgets/register_page.dart";
class Welcome extends StatelessWidget {
final String logo = "assets/logo.png";
@ -26,14 +29,6 @@ class Welcome extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Image.asset(logo,
semanticLabel: "Beep logo", width: 30.0, height: 50.0),
Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text("Beep",
style: Theme.of(context).accentTextTheme.display3)),
]),
Expanded(
child: Navigator(
initialRoute: "welcome/hello",
@ -43,14 +38,16 @@ class Welcome extends StatelessWidget {
case "welcome/hello":
builder = (BuildContext _) => WelcomePage();
break;
case "welcome/register":
builder = (BuildContext _) =>
RegisterPage(loginManager: loginManager);
break;
case "welcome/login":
builder = (BuildContext _) =>
LoginPage(loginManager: loginManager);
break;
case "welcome/otp":
builder = (BuildContext _) =>
OtpPage(buttonCallback: (String otp) async {
// loginManager.processOtp(otp); disabled for testing
builder = (BuildContext _) => OtpPage(buttonCallback: () {
Navigator.of(context).pushReplacementNamed("/home");
});
break;

View File

@ -0,0 +1,30 @@
import "package:flutter/material.dart";
class Input extends StatelessWidget {
final TextEditingController controller;
final String hintText;
Input({@required this.controller, @required this.hintText});
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Row(mainAxisSize: MainAxisSize.max, children: <Widget>[
Expanded(
child: TextField(
controller: controller,
autocorrect: false,
cursorWidth: 2.0,
cursorColor: Colors.white,
style: Theme.of(context).accentTextTheme.title,
decoration: InputDecoration(
border: InputBorder.none,
filled: true,
fillColor: Color(0x10000000),
hintText: hintText,
hintStyle: Theme.of(context).accentTextTheme.title,
))),
]));
}
}

View File

@ -3,7 +3,6 @@ import "package:flutter_svg/flutter_svg.dart";
import "../../widgets/text_button.dart";
import "../../../services/login_manager.dart";
import "../../../services/conversation_manager.dart";
import "phone_input.dart";
class LoginPage extends StatefulWidget {
@ -30,39 +29,41 @@ class _LoginPageState extends State<LoginPage> {
return Padding(
padding: EdgeInsets.only(left: 15.0, right: 15.0),
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Expanded(
child: SingleChildScrollView(
child:
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10.0, bottom: 20.0),
child: SvgPicture.asset(phoneSvg,
height: MediaQuery.of(context).size.height / 5)),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("First things first.",
textAlign: TextAlign.left,
style: Theme.of(context).accentTextTheme.display3),
Text(
"Enter your phone number, to connect to your Beep account.",
style: Theme.of(context)
.accentTextTheme
.title
.copyWith(fontWeight: FontWeight.w400)),
Padding(
padding: EdgeInsets.only(top: 20.0),
child: PhoneInput(controller: controller)),
])
]))),
Padding(
padding: EdgeInsets.only(top: 10.0, bottom: 20.0),
child: SvgPicture.asset(phoneSvg,
height: MediaQuery.of(context).size.height / 5)),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("First things first.",
textAlign: TextAlign.left,
style: Theme.of(context).accentTextTheme.display3),
Padding(
padding: EdgeInsets.only(top: 5.0),
child: Text(
"Enter your phone number, to connect to your existing Beep account.",
style: Theme.of(context).accentTextTheme.title)),
Padding(
padding: EdgeInsets.only(top: 20.0),
child: PhoneInput(controller: controller)),
]),
Spacer(),
TextButton(
text: "Continue",
onClickCallback: () async {
final authToken =
await widget.loginManager.loginTest(controller.text);
// Waiting for initialization
await ConversationManager.init(authToken);
print(authToken);
Navigator.pushNamed(context, 'welcome/otp');
}),
padding: EdgeInsets.only(top: 10.0),
child: TextButton(
text: "Continue",
onClickCallback: () async {
await widget.loginManager
.initAuthentication("+65${controller.text}");
Navigator.pushNamed(context, 'welcome/otp');
})),
]));
}
}

View File

@ -2,10 +2,13 @@ import "package:flutter/material.dart";
import 'package:pin_code_text_field/pin_code_text_field.dart';
import "package:flutter_svg/flutter_svg.dart";
import "../../../services/conversation_manager.dart";
import "../../../services/login_manager.dart";
import "../../widgets/text_button.dart";
// Callback types
typedef void ButtonCallback(String otp);
typedef void ButtonCallback();
class OtpPage extends StatefulWidget {
final ButtonCallback buttonCallback;
@ -18,6 +21,7 @@ class OtpPage extends StatefulWidget {
class _OtpPageState extends State<OtpPage> {
final String phoneSvg = "assets/authenticate.svg";
final LoginManager loginManager = LoginManager();
final controller = TextEditingController();
@override
@ -46,9 +50,18 @@ class _OtpPageState extends State<OtpPage> {
child: Center(
child: PinCodeTextField(
controller: controller,
maxLength: 6,
highlight: true,
autofocus: true,
highlightColor: Colors.white,
/*hideCharacter: true,*/
onTextChanged: (text) {},
onDone: (text) {
// widget.buttonCallback(text);
},
pinBoxHeight:
(MediaQuery.of(context).size.width / 6) - 15,
pinBoxWidth:
(MediaQuery.of(context).size.width / 6) - 15,
pinBoxDecoration: (Color color) {
return BoxDecoration(
color: Color(0x10000000),
@ -61,7 +74,18 @@ class _OtpPageState extends State<OtpPage> {
Spacer(),
TextButton(
text: "Done",
onClickCallback: () => widget.buttonCallback(controller.text)),
onClickCallback: () async {
final authToken =
await loginManager.processOtp(controller.text);
final user = await loginManager.getUser();
await ConversationManager.init(authToken);
if (user.firstName == "" && user.lastName == "") {
Navigator.pushNamed(context, 'welcome/register');
} else {
widget.buttonCallback();
}
}),
]));
}
}

View File

@ -0,0 +1,86 @@
import "package:flutter/material.dart";
import "package:flutter_svg/flutter_svg.dart";
import "../../widgets/text_button.dart";
import "../../../services/login_manager.dart";
import "../../../resources/user_api_provider.dart";
import "input.dart";
class RegisterPage extends StatefulWidget {
final LoginManager loginManager;
RegisterPage({@required this.loginManager});
@override
_RegisterPageState createState() => _RegisterPageState();
}
class _RegisterPageState extends State<RegisterPage> {
final String phoneSvg = "assets/phoneno.svg";
final firstNameController = TextEditingController();
final lastNameController = TextEditingController();
final userApiProvider = UserApiProvider();
@override
void dispose() {
firstNameController.dispose();
lastNameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 15.0, right: 15.0),
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Expanded(
child: SingleChildScrollView(
child:
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10.0, bottom: 20.0),
child: SvgPicture.asset(phoneSvg,
height: MediaQuery.of(context).size.height / 5)),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Let's get you going.",
textAlign: TextAlign.left,
style: Theme.of(context).accentTextTheme.display3),
Text("Enter your info to create your very own Beep account.",
style: Theme.of(context)
.accentTextTheme
.title
.copyWith(fontWeight: FontWeight.w400)),
Padding(
padding: EdgeInsets.only(top: 20.0),
child: Input(
controller: firstNameController,
hintText: "First name")),
Padding(
padding: EdgeInsets.only(top: 10.0),
child: Input(
controller: lastNameController,
hintText: "Last name")),
])
]))),
Padding(
padding: EdgeInsets.only(top: 10.0),
child: TextButton(
text: "Continue",
onClickCallback: () async {
final firstName = firstNameController.text;
final lastName = lastNameController.text;
// Creating the new user
await userApiProvider.updateUser(
firstName, lastName);
Navigator.of(context).pushReplacementNamed("/home");
})),
]));
}
}

View File

@ -5,12 +5,21 @@ import "../../widgets/text_button.dart";
class WelcomePage extends StatelessWidget {
final String welcomeSvg = "assets/welcome.svg";
final String logo = "assets/logo.png";
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 15.0, right: 15.0),
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Image.asset(logo,
semanticLabel: "Beep logo", width: 30.0, height: 50.0),
Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text("Beep",
style: Theme.of(context).accentTextTheme.display3)),
]),
Spacer(),
SvgPicture.asset(welcomeSvg,
width: MediaQuery.of(context).size.width - 40.0),
@ -25,7 +34,6 @@ class WelcomePage extends StatelessWidget {
text: "Login",
onClickCallback: () =>
Navigator.pushNamed(context, "welcome/login")),
TextButton(text: "Sign Up", onClickCallback: () => print("hello")),
]));
}
}