| @@ -1,24 +1,20 @@ | |||
| import 'package:authentication_repository/authentication_repository.dart'; | |||
| import 'package:farm_tpf/presentation/screens/home/view/home_page.dart'; | |||
| import 'package:farm_tpf/presentation/screens/login/view/login_page.dart'; | |||
| import 'package:farm_tpf/presentation/screens/splash/view/splash_page.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'package:user_repository/user_repository.dart'; | |||
| import 'authentication/bloc/authentication_bloc.dart'; | |||
| import 'data/repository/authentication_repository.dart'; | |||
| class App extends StatelessWidget { | |||
| const App({ | |||
| Key key, | |||
| @required this.authenticationRepository, | |||
| @required this.userRepository, | |||
| }) : assert(authenticationRepository != null), | |||
| assert(userRepository != null), | |||
| super(key: key); | |||
| final AuthenticationRepository authenticationRepository; | |||
| final UserRepository userRepository; | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| @@ -27,7 +23,6 @@ class App extends StatelessWidget { | |||
| child: BlocProvider( | |||
| create: (_) => AuthenticationBloc( | |||
| authenticationRepository: authenticationRepository, | |||
| userRepository: userRepository, | |||
| ), | |||
| child: AppView(), | |||
| ), | |||
| @@ -1,23 +1,19 @@ | |||
| import 'dart:async'; | |||
| import 'package:authentication_repository/authentication_repository.dart'; | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/data/repository/authentication_repository.dart'; | |||
| import 'package:meta/meta.dart'; | |||
| import 'package:user_repository/user_repository.dart'; | |||
| part 'authentication_event.dart'; | |||
| part 'authentication_state.dart'; | |||
| class AuthenticationBloc | |||
| extends Bloc<AuthenticationEvent, AuthenticationState> { | |||
| AuthenticationBloc({ | |||
| @required AuthenticationRepository authenticationRepository, | |||
| @required UserRepository userRepository, | |||
| }) : assert(authenticationRepository != null), | |||
| assert(userRepository != null), | |||
| AuthenticationBloc( | |||
| {@required AuthenticationRepository authenticationRepository}) | |||
| : assert(authenticationRepository != null), | |||
| _authenticationRepository = authenticationRepository, | |||
| _userRepository = userRepository, | |||
| super(const AuthenticationState.unknown()) { | |||
| _authenticationStatusSubscription = _authenticationRepository.status.listen( | |||
| (status) => add(AuthenticationStatusChanged(status)), | |||
| @@ -25,7 +21,6 @@ class AuthenticationBloc | |||
| } | |||
| final AuthenticationRepository _authenticationRepository; | |||
| final UserRepository _userRepository; | |||
| StreamSubscription<AuthenticationStatus> _authenticationStatusSubscription; | |||
| @override | |||
| @@ -53,21 +48,9 @@ class AuthenticationBloc | |||
| case AuthenticationStatus.unauthenticated: | |||
| return const AuthenticationState.unauthenticated(); | |||
| case AuthenticationStatus.authenticated: | |||
| final user = await _tryGetUser(); | |||
| return user != null | |||
| ? AuthenticationState.authenticated(user) | |||
| : const AuthenticationState.unauthenticated(); | |||
| return AuthenticationState.authenticated(); | |||
| default: | |||
| return const AuthenticationState.unknown(); | |||
| } | |||
| } | |||
| Future<User> _tryGetUser() async { | |||
| try { | |||
| final user = await _userRepository.getUser(); | |||
| return user; | |||
| } on Exception { | |||
| return null; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,22 +1,18 @@ | |||
| part of 'authentication_bloc.dart'; | |||
| class AuthenticationState extends Equatable { | |||
| const AuthenticationState._({ | |||
| this.status = AuthenticationStatus.unknown, | |||
| this.user, | |||
| }); | |||
| const AuthenticationState._({this.status = AuthenticationStatus.unknown}); | |||
| const AuthenticationState.unknown() : this._(); | |||
| const AuthenticationState.authenticated(User user) | |||
| : this._(status: AuthenticationStatus.authenticated, user: user); | |||
| const AuthenticationState.authenticated() | |||
| : this._(status: AuthenticationStatus.authenticated); | |||
| const AuthenticationState.unauthenticated() | |||
| : this._(status: AuthenticationStatus.unauthenticated); | |||
| final AuthenticationStatus status; | |||
| final User user; | |||
| @override | |||
| List<Object> get props => [status, user]; | |||
| List<Object> get props => [status]; | |||
| } | |||
| @@ -0,0 +1,51 @@ | |||
| import 'dart:async'; | |||
| import 'package:farm_tpf/authentication/authentication.dart'; | |||
| import 'package:farm_tpf/data/api/dio_provider.dart'; | |||
| import 'package:farm_tpf/data/api/rest_client.dart'; | |||
| import 'package:farm_tpf/models/user.dart'; | |||
| import 'package:farm_tpf/models/user_request.dart'; | |||
| import 'package:farm_tpf/utils/const_common.dart'; | |||
| import 'package:farm_tpf/utils/pref.dart'; | |||
| import 'package:meta/meta.dart'; | |||
| enum AuthenticationStatus { unknown, authenticated, unauthenticated } | |||
| class AuthenticationRepository { | |||
| final _controller = StreamController<AuthenticationStatus>(); | |||
| final dio = DioProvider.instance(); | |||
| final pref = LocalPref(); | |||
| Stream<AuthenticationStatus> get status async* { | |||
| try { | |||
| var token = await pref.getString(PrefKey.token_key); | |||
| var expiredTime = await pref.getString(PrefKey.expired_time); | |||
| int currentTime = DateTime.now().millisecondsSinceEpoch; | |||
| bool isNotExpired = | |||
| (currentTime - int.tryParse(expiredTime)) < ConstCommon.kExpiredTime; | |||
| if (token.isNotEmpty && isNotExpired) { | |||
| yield AuthenticationStatus.authenticated; | |||
| } else { | |||
| yield AuthenticationStatus.unauthenticated; | |||
| } | |||
| } catch (_) { | |||
| yield AuthenticationStatus.unauthenticated; | |||
| } | |||
| yield* _controller.stream; | |||
| } | |||
| Future<User> signInWithCredentials(String username, String password) { | |||
| final client = RestClient(dio); | |||
| var result = | |||
| client.login(UserRequest(username: username, password: password)); | |||
| return result; | |||
| } | |||
| void logOut() { | |||
| pref.saveString(PrefKey.token_key, ""); | |||
| _controller.add(AuthenticationStatus.unauthenticated); | |||
| } | |||
| void dispose() => _controller.close(); | |||
| } | |||
| @@ -1,11 +1,7 @@ | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:authentication_repository/authentication_repository.dart'; | |||
| import 'package:user_repository/user_repository.dart'; | |||
| import 'app.dart'; | |||
| import 'data/repository/authentication_repository.dart'; | |||
| void main() { | |||
| runApp(App( | |||
| authenticationRepository: AuthenticationRepository(), | |||
| userRepository: UserRepository(), | |||
| )); | |||
| runApp(App(authenticationRepository: AuthenticationRepository())); | |||
| } | |||
| @@ -15,9 +15,9 @@ class HomePage extends StatelessWidget { | |||
| child: Column( | |||
| mainAxisSize: MainAxisSize.min, | |||
| children: <Widget>[ | |||
| Text( | |||
| 'UserID: ${context.bloc<AuthenticationBloc>().state.user.id}', | |||
| ), | |||
| Text("logged in." | |||
| // 'UserID: ${context.bloc<AuthenticationBloc>().state.user.id}', | |||
| ), | |||
| RaisedButton( | |||
| child: const Text('Đăng xuất'), | |||
| onPressed: () { | |||
| @@ -1,10 +1,10 @@ | |||
| import 'dart:async'; | |||
| import 'package:authentication_repository/authentication_repository.dart'; | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/data/repository/authentication_repository.dart'; | |||
| import 'package:farm_tpf/presentation/screens/login/models/password.dart'; | |||
| import 'package:farm_tpf/presentation/screens/login/models/username.dart'; | |||
| import 'package:farm_tpf/utils/pref.dart'; | |||
| import 'package:formz/formz.dart'; | |||
| import 'package:meta/meta.dart'; | |||
| @@ -19,6 +19,7 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> { | |||
| super(const LoginState()); | |||
| final AuthenticationRepository _authenticationRepository; | |||
| var pref = LocalPref(); | |||
| @override | |||
| Stream<LoginState> mapEventToState( | |||
| @@ -62,10 +63,15 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> { | |||
| if (state.status.isValidated) { | |||
| yield state.copyWith(status: FormzStatus.submissionInProgress); | |||
| try { | |||
| await _authenticationRepository.logIn( | |||
| username: state.username.value, | |||
| password: state.password.value, | |||
| var user = await _authenticationRepository.signInWithCredentials( | |||
| state.username.value, | |||
| state.password.value, | |||
| ); | |||
| var token = user.idToken; | |||
| pref.saveString(PrefKey.token_key, token); | |||
| int currentTime = DateTime.now().millisecondsSinceEpoch; | |||
| pref.saveString(PrefKey.expired_time, currentTime.toString()); | |||
| yield state.copyWith(status: FormzStatus.submissionSuccess); | |||
| } on Exception catch (_) { | |||
| yield state.copyWith(status: FormzStatus.submissionFailure); | |||
| @@ -1,20 +1,35 @@ | |||
| import 'package:farm_tpf/authentication/authentication.dart'; | |||
| import 'package:farm_tpf/data/repository/authentication_repository.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/widget_loading.dart'; | |||
| import 'package:farm_tpf/presentation/screens/login/bloc/login_bloc.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:formz/formz.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| class LoginForm extends StatelessWidget { | |||
| AuthenticationBloc _authenticationBloc; | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| _authenticationBloc = BlocProvider.of<AuthenticationBloc>(context); | |||
| return BlocListener<LoginBloc, LoginState>( | |||
| listener: (context, state) { | |||
| if (state.status.isSubmissionFailure) { | |||
| LoadingDialog.hideLoadingDialog(context); | |||
| Scaffold.of(context) | |||
| ..hideCurrentSnackBar() | |||
| ..showSnackBar( | |||
| const SnackBar(content: Text('Authentication Failure')), | |||
| ); | |||
| } | |||
| if (state.status.isSubmissionSuccess) { | |||
| LoadingDialog.hideLoadingDialog(context); | |||
| _authenticationBloc.add( | |||
| AuthenticationStatusChanged(AuthenticationStatus.authenticated)); | |||
| } | |||
| if (state.status.isSubmissionInProgress) { | |||
| LoadingDialog.showLoadingDialog(context); | |||
| } | |||
| }, | |||
| child: Align( | |||
| alignment: const Alignment(0, -1 / 3), | |||
| @@ -1,4 +1,4 @@ | |||
| import 'package:authentication_repository/authentication_repository.dart'; | |||
| import 'package:farm_tpf/data/repository/authentication_repository.dart'; | |||
| import 'package:farm_tpf/presentation/screens/login/bloc/login_bloc.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| @@ -1,3 +0,0 @@ | |||
| library authentication_repository; | |||
| export 'src/authentication_repository.dart'; | |||
| @@ -1,34 +0,0 @@ | |||
| import 'dart:async'; | |||
| import 'package:meta/meta.dart'; | |||
| enum AuthenticationStatus { unknown, authenticated, unauthenticated } | |||
| class AuthenticationRepository { | |||
| final _controller = StreamController<AuthenticationStatus>(); | |||
| Stream<AuthenticationStatus> get status async* { | |||
| await Future<void>.delayed(const Duration(seconds: 1)); | |||
| yield AuthenticationStatus.unauthenticated; | |||
| yield* _controller.stream; | |||
| } | |||
| Future<void> logIn({ | |||
| @required String username, | |||
| @required String password, | |||
| }) async { | |||
| assert(username != null); | |||
| assert(password != null); | |||
| await Future.delayed( | |||
| const Duration(milliseconds: 300), | |||
| () => _controller.add(AuthenticationStatus.authenticated), | |||
| ); | |||
| } | |||
| void logOut() { | |||
| _controller.add(AuthenticationStatus.unauthenticated); | |||
| } | |||
| void dispose() => _controller.close(); | |||
| } | |||
| @@ -1,12 +0,0 @@ | |||
| # Generated by pub | |||
| # See https://dart.dev/tools/pub/glossary#lockfile | |||
| packages: | |||
| meta: | |||
| dependency: "direct main" | |||
| description: | |||
| name: meta | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.2" | |||
| sdks: | |||
| dart: ">=2.1.0 <3.0.0" | |||
| @@ -1,9 +0,0 @@ | |||
| name: authentication_repository | |||
| description: Dart package which manages the authentication domain. | |||
| version: 1.0.0 | |||
| environment: | |||
| sdk: ">=2.1.0 <3.0.0" | |||
| dependencies: | |||
| meta: ^1.1.8 | |||
| @@ -1 +0,0 @@ | |||
| export 'user.dart'; | |||
| @@ -1,10 +0,0 @@ | |||
| import 'package:equatable/equatable.dart'; | |||
| class User extends Equatable { | |||
| const User(this.id); | |||
| final String id; | |||
| @override | |||
| List<Object> get props => [id]; | |||
| } | |||
| @@ -1,14 +0,0 @@ | |||
| import 'dart:async'; | |||
| import 'models/models.dart'; | |||
| class UserRepository { | |||
| User _user; | |||
| Future<User> getUser() async { | |||
| if (_user != null) return _user; | |||
| return Future.delayed( | |||
| const Duration(milliseconds: 300), | |||
| () => _user = User("usertest"), | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,4 +0,0 @@ | |||
| library user_repository; | |||
| export 'src/models/models.dart'; | |||
| export 'src/user_repository.dart'; | |||
| @@ -1,61 +0,0 @@ | |||
| # Generated by pub | |||
| # See https://dart.dev/tools/pub/glossary#lockfile | |||
| packages: | |||
| charcode: | |||
| dependency: transitive | |||
| description: | |||
| name: charcode | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.1.3" | |||
| collection: | |||
| dependency: transitive | |||
| description: | |||
| name: collection | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.14.13" | |||
| convert: | |||
| dependency: transitive | |||
| description: | |||
| name: convert | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.1.1" | |||
| crypto: | |||
| dependency: transitive | |||
| description: | |||
| name: crypto | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.1.5" | |||
| equatable: | |||
| dependency: "direct main" | |||
| description: | |||
| name: equatable | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.3" | |||
| meta: | |||
| dependency: "direct main" | |||
| description: | |||
| name: meta | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.2" | |||
| typed_data: | |||
| dependency: transitive | |||
| description: | |||
| name: typed_data | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.0" | |||
| uuid: | |||
| dependency: "direct main" | |||
| description: | |||
| name: uuid | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.2.0" | |||
| sdks: | |||
| dart: ">=2.4.0 <3.0.0" | |||
| @@ -1,11 +0,0 @@ | |||
| name: user_repository | |||
| description: Dart package which manages the user domain. | |||
| version: 1.0.0 | |||
| environment: | |||
| sdk: ">=2.1.0 <3.0.0" | |||
| dependencies: | |||
| equatable: ^1.2.0 | |||
| meta: ^1.1.8 | |||
| uuid: ^2.1.0 | |||
| @@ -36,13 +36,6 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.4.1" | |||
| authentication_repository: | |||
| dependency: "direct main" | |||
| description: | |||
| path: "packages/authentication_repository" | |||
| relative: true | |||
| source: path | |||
| version: "1.0.0" | |||
| bloc: | |||
| dependency: transitive | |||
| description: | |||
| @@ -609,20 +602,6 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.1.6" | |||
| user_repository: | |||
| dependency: "direct main" | |||
| description: | |||
| path: "packages/user_repository" | |||
| relative: true | |||
| source: path | |||
| version: "1.0.0" | |||
| uuid: | |||
| dependency: transitive | |||
| description: | |||
| name: uuid | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.2.0" | |||
| vector_math: | |||
| dependency: transitive | |||
| description: | |||
| @@ -18,11 +18,6 @@ dependencies: | |||
| dio: 3.0.9 | |||
| formz: ^0.3.0 | |||
| authentication_repository: | |||
| path: packages/authentication_repository | |||
| user_repository: | |||
| path: packages/user_repository | |||
| dev_dependencies: | |||
| flutter_test: | |||
| sdk: flutter | |||