Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT: adiciona o command ao login (#58) #61

Merged
merged 3 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions lib/ui/home/view/HomeView.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ class HomeView extends StatelessWidget {

@override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
return const Text('Home', style: TextStyle(fontSize: 20));
}


Expand Down
111 changes: 52 additions & 59 deletions lib/ui/login/view/login_view.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:aranduapp/core/log/Log.dart';
import 'package:aranduapp/ui/navbar/view/navBarView.dart';
import 'package:aranduapp/ui/shared/TextAndLink.dart';
import 'package:aranduapp/ui/shared/requestbutton.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
Expand All @@ -21,61 +23,49 @@ class Login extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => LoginViewModel(context),
create: (context) => LoginViewModel(),
child: const LoginScreen(),
);
}
}

class LoginScreen extends StatefulWidget {
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});

@override
State<StatefulWidget> createState() {
return _LoginScreenState();
}
}

class _LoginScreenState extends State<LoginScreen> {
late Future<void> _future;

@override
void initState() {
super.initState();
_future =
Provider.of<LoginViewModel>(context, listen: false).validateToken();
}

@override
Widget build(BuildContext context) {
LoginViewModel viewModel = Provider.of<LoginViewModel>(context);

return Scaffold(
body: FutureBuilder(
future: _future,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return _loadingScreen(viewModel);
} else if (!snapshot.hasError) {
return _authDevice(viewModel);
} else {
return _emailAndPassword(viewModel);
}
}));
body: ListenableBuilder(
listenable: viewModel.validadeTokenCommand,
builder: (context, child) {
if (viewModel.validadeTokenCommand.isOk) {
return _authDevice(viewModel, context);
} else if (viewModel.validadeTokenCommand.isError) {
return _emailAndPassword(viewModel, context);
} else {
return _loadingScreen(viewModel, context);
}
},
),
);
}

Widget _loadingScreen(LoginViewModel viewModel) {
Widget _loadingScreen(LoginViewModel viewModel, BuildContext context) {
return const Center(
child: CircularProgressIndicator(value: null),
);
}

Widget _authDevice(LoginViewModel viewModel) {
Widget _authDevice(LoginViewModel viewModel, BuildContext context) {
Log.d("Mostrando tela de autorização do dispositivo");

viewModel.loginWithDeviceAuth().then((ok) {
if (ok) {
viewModel.goToHome();
WidgetsBinding.instance.addPostFrameCallback((_) {
goToNavbar(context);
});
}
});

Expand All @@ -99,7 +89,9 @@ class _LoginScreenState extends State<LoginScreen> {
child: ElevatedButton(
onPressed: () async {
viewModel.loginWithDeviceAuth().then((ok) {
viewModel.goToHome();
WidgetsBinding.instance.addPostFrameCallback((_) {
goToNavbar(context);
});
});
},
child: const Text('Usar senha do celular'),
Expand All @@ -110,7 +102,7 @@ class _LoginScreenState extends State<LoginScreen> {
);
}

Widget _emailAndPassword(LoginViewModel viewModel) {
Widget _emailAndPassword(LoginViewModel viewModel, BuildContext context) {
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
Expand All @@ -120,11 +112,11 @@ class _LoginScreenState extends State<LoginScreen> {
const SizedBox(height: 80),
const SizedBox(height: 10),
_formSection(viewModel),
_forgotPasswordLink(),
_forgotPasswordLink(context),
const SizedBox(height: 80),
_loginButtonSection(),
_loginButtonSection(context),
const OrDivider(),
_loggingInWithOther(),
_loggingInWithOther(context),
TextAndLink(
text: 'É novo pro aqui?',
link: 'Cria a sua conta',
Expand Down Expand Up @@ -157,7 +149,7 @@ class _LoginScreenState extends State<LoginScreen> {
);
}

Widget _forgotPasswordLink() {
Widget _forgotPasswordLink(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(
Expand All @@ -182,31 +174,24 @@ class _LoginScreenState extends State<LoginScreen> {
);
}

Widget _loginButtonSection() {
Widget _loginButtonSection(BuildContext context) {
LoginViewModel viewModel = Provider.of<LoginViewModel>(context);

return SizedBox(
width: 291,
height: 64,
child: ElevatedButton(
onPressed: () {
viewModel.loginWithEmailAndPassword().then((_) {
viewModel.goToHome();
}).catchError((e) => showDialog<Object>(
context: context,
builder: (BuildContext context) =>
ErrorPopUp(content: Text('$e')),
));
},
child: Consumer<LoginViewModel>(
builder: (context, value, child) => value.isLoading
? const CircularProgressIndicator(value: null)
: const Text('Entrar'),
)),
);
return Requestbutton(
command: viewModel.loginCommand,
onErrorCallback: (String e) {
showDialog<Object>(
context: context,
builder: (BuildContext context) => ErrorPopUp(content: Text(e)),
);
},
onSuccessCallback: () {
goToNavbar(context);
},
nameButton: 'Entrar');
}

Widget _loggingInWithOther() {
Widget _loggingInWithOther(BuildContext context) {
return GestureDetector(
onTap: () => Log.d(""),
child: Container(
Expand All @@ -225,4 +210,12 @@ class _LoginScreenState extends State<LoginScreen> {
),
);
}

void goToNavbar(BuildContext context) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => const NavbarView(),
),
);
}
}
63 changes: 22 additions & 41 deletions lib/ui/login/viewModel/login_view_model.dart
Original file line number Diff line number Diff line change
@@ -1,71 +1,52 @@
import 'package:aranduapp/core/log/Log.dart';
import 'package:aranduapp/core/state/command.dart';
import 'package:aranduapp/ui/navbar/view/navBarView.dart';
import 'package:async/async.dart';
import 'package:flutter/material.dart';
import 'package:local_auth/local_auth.dart';
import 'package:aranduapp/ui/login/service/LoginService.dart';
import 'package:aranduapp/ui/login/model/LoginRequest.dart';

class LoginViewModel extends ChangeNotifier {
final BuildContext context;

bool isLoading;
late Command0<void> loginCommand;
late Command0<void> validadeTokenCommand;

final GlobalKey<FormState> formKey;
final TextEditingController emailController;
final TextEditingController passwordController;

LoginViewModel(this.context)
: isLoading = false,
formKey = GlobalKey<FormState>(),
LoginViewModel()
: formKey = GlobalKey<FormState>(),
emailController = TextEditingController(),
passwordController = TextEditingController();
passwordController = TextEditingController() {

Future<void> loginWithEmailAndPassword() async {
// TODO use mutex to make this
if (isLoading) {
return;
}

try {
isLoading = true;
super.notifyListeners();
loginCommand = Command0<void>(loginWithEmailAndPassword);

if (!formKey.currentState!.validate()) {
throw Exception('Valores inválidos');
}
validadeTokenCommand = Command0<void>(validateToken);
validadeTokenCommand.execute();
}

await LoginService.login(
LoginRequest(emailController.text, passwordController.text));
} catch (e) {
rethrow;
} finally {
isLoading = false;
notifyListeners();
Future<Result<void>> loginWithEmailAndPassword() async {
if (!formKey.currentState!.validate()) {
return Result.error(Exception('Valores inválidos'));
}

await LoginService.login(
LoginRequest(emailController.text, passwordController.text));

return Result.value(null);
}

Future<void> validateToken() async {
Future<Result<void>> validateToken() async {
await LoginService.validateToken();

return Result.value(null);
}

Future<bool> loginWithDeviceAuth() async {
Log.d('init loginWithDeviceAuth');
return await LocalAuthentication()
.authenticate(localizedReason: 'Toque com o dedo no sensor para logar');
}

void goToHome() {
try {
if (context.mounted) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => const NavbarView(),
),
);
}
} catch (e) {
Log.e(e);
rethrow;
}
}
}
Loading
Loading