From 3eb473cd9dcd0dca4478d5e16a7de1d28e03bc13 Mon Sep 17 00:00:00 2001 From: leynier Date: Mon, 26 Apr 2021 01:55:31 -0400 Subject: [PATCH] Add initial message, faqs page and fix registration success message --- analysis_options.yaml | 2 + lib/src/data/enums/home_item_enum.dart | 1 + lib/src/data/local/local_storage.dart | 14 + lib/src/data/models/status.dart | 11 +- lib/src/data/models/status.g.dart | 17 ++ lib/src/dependencies.dart | 1 + .../blocs/home_bloc/home_bloc.dart | 1 + .../blocs/login_bloc/login_bloc.dart | 13 +- .../blocs/login_bloc/login_bloc.freezed.dart | 252 +++++++++++++++--- .../blocs/login_bloc/login_event.dart | 1 + .../blocs/login_bloc/login_state.dart | 1 + .../blocs/register_bloc/register_bloc.dart | 2 +- lib/src/presentation/pages/faqs_page.dart | 43 +++ .../pages/home_page/home_page.dart | 20 +- .../pages/home_page/sub_pages/about_page.dart | 22 +- .../sub_pages/helpful_links_page.dart | 12 +- lib/src/presentation/pages/login_page.dart | 42 ++- lib/src/presentation/pages/pages.dart | 1 + .../pages/recover_password_page.dart | 11 +- lib/src/presentation/pages/register_page.dart | 119 ++++++--- lib/src/router.dart | 25 +- lib/src/utils/constants/constants.dart | 1 + lib/src/utils/constants/faqs.dart | 22 ++ lib/src/utils/constants/messages.dart | 10 + lib/src/utils/constants/routes.dart | 1 + lib/src/utils/constants/storage_keys.dart | 1 + lib/src/utils/open_url.dart | 14 + pubspec.yaml | 2 +- 28 files changed, 531 insertions(+), 131 deletions(-) create mode 100644 lib/src/data/models/status.g.dart create mode 100644 lib/src/presentation/pages/faqs_page.dart create mode 100644 lib/src/utils/constants/faqs.dart create mode 100644 lib/src/utils/open_url.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index a3258d1..ff8a731 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -8,6 +8,8 @@ analyzer: - lib/**/*.freezed.dart strong-mode: implicit-casts: false + errors: + todo: warning linter: rules: diff --git a/lib/src/data/enums/home_item_enum.dart b/lib/src/data/enums/home_item_enum.dart index 87a43fe..b966fa8 100644 --- a/lib/src/data/enums/home_item_enum.dart +++ b/lib/src/data/enums/home_item_enum.dart @@ -6,5 +6,6 @@ enum HomeItemEnum { ResetPassword, AboutUs, HelpfulLinks, + Faqs, Logout, } diff --git a/lib/src/data/local/local_storage.dart b/lib/src/data/local/local_storage.dart index 5840f09..05240b4 100644 --- a/lib/src/data/local/local_storage.dart +++ b/lib/src/data/local/local_storage.dart @@ -23,6 +23,10 @@ abstract class ILocalStorage { }); Future invalidateCredentials(); + + Future isFirstTime(); + + Future useFirstTime(); } class LocalStorage implements ILocalStorage { @@ -111,6 +115,16 @@ class LocalStorage implements ILocalStorage { sessionData.isLoggedInto = false; sessionData.rememberMe = false; } + + @override + Future isFirstTime() async { + return prefs.getBool(APP_FIRST_TIME) ?? true; + } + + @override + Future useFirstTime() async { + await prefs.setBool(APP_FIRST_TIME, false); + } } class SessionData { diff --git a/lib/src/data/models/status.dart b/lib/src/data/models/status.dart index bfe361d..5c92bee 100644 --- a/lib/src/data/models/status.dart +++ b/lib/src/data/models/status.dart @@ -1,13 +1,16 @@ import 'package:gestionuh/src/data/models/base_model.dart'; +import 'package:json_annotation/json_annotation.dart'; +part 'status.g.dart'; + +@JsonSerializable() class Status extends BaseModel { bool? status; Status({this.status}); + factory Status.fromJson(Map json) => _$StatusFromJson(json); + @override - Map toJson() { - // TODO: implement toJson - throw UnimplementedError(); - } + Map toJson() => _$StatusToJson(this); } diff --git a/lib/src/data/models/status.g.dart b/lib/src/data/models/status.g.dart new file mode 100644 index 0000000..4dfe2e4 --- /dev/null +++ b/lib/src/data/models/status.g.dart @@ -0,0 +1,17 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'status.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Status _$StatusFromJson(Map json) { + return Status( + status: json['status'] as bool?, + ); +} + +Map _$StatusToJson(Status instance) => { + 'status': instance.status, + }; diff --git a/lib/src/dependencies.dart b/lib/src/dependencies.dart index 0555bcb..9971dfc 100644 --- a/lib/src/dependencies.dart +++ b/lib/src/dependencies.dart @@ -74,6 +74,7 @@ class DependencyInjection { I.registerFactory( () => LoginBloc( authRepository: I(), + localStorage: I(), ), ); diff --git a/lib/src/presentation/blocs/home_bloc/home_bloc.dart b/lib/src/presentation/blocs/home_bloc/home_bloc.dart index 2baf466..471e852 100644 --- a/lib/src/presentation/blocs/home_bloc/home_bloc.dart +++ b/lib/src/presentation/blocs/home_bloc/home_bloc.dart @@ -94,6 +94,7 @@ class HomeBloc extends Bloc { if (profile.hasEmail ?? false) HomeItemEnum.MailQuota, HomeItemEnum.ResetPassword, HomeItemEnum.HelpfulLinks, + HomeItemEnum.Faqs, HomeItemEnum.AboutUs, HomeItemEnum.Separator, HomeItemEnum.Logout, diff --git a/lib/src/presentation/blocs/login_bloc/login_bloc.dart b/lib/src/presentation/blocs/login_bloc/login_bloc.dart index c9d0adb..62819ee 100644 --- a/lib/src/presentation/blocs/login_bloc/login_bloc.dart +++ b/lib/src/presentation/blocs/login_bloc/login_bloc.dart @@ -1,26 +1,37 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:gestionuh/src/data/local/local_storage.dart'; import 'package:gestionuh/src/data/repositories/repositories.dart'; import 'package:gestionuh/src/utils/constants/constants.dart'; +part 'login_bloc.freezed.dart'; part 'login_event.dart'; part 'login_state.dart'; -part 'login_bloc.freezed.dart'; class LoginBloc extends Bloc { final AuthRepository authRepository; + final ILocalStorage localStorage; LoginBloc({ required this.authRepository, + required this.localStorage, }) : super(const LoginState.initial()); @override Stream mapEventToState(LoginEvent event) async* { yield* event.map( + start: loginStart, loginAttempted: loginAttemptedHandler, ); } + Stream loginStart(_$LoginStart event) async* { + if (await localStorage.isFirstTime()) { + await localStorage.useFirstTime(); + yield const LoginState.firstTime(); + } + } + Stream loginAttemptedHandler(_$LoginAttempted event) async* { yield const LoginState.inProgress(); final result = await authRepository.login( diff --git a/lib/src/presentation/blocs/login_bloc/login_bloc.freezed.dart b/lib/src/presentation/blocs/login_bloc/login_bloc.freezed.dart index 1adbd1d..1c8ade7 100644 --- a/lib/src/presentation/blocs/login_bloc/login_bloc.freezed.dart +++ b/lib/src/presentation/blocs/login_bloc/login_bloc.freezed.dart @@ -16,6 +16,10 @@ final _privateConstructorUsedError = UnsupportedError( class _$LoginEventTearOff { const _$LoginEventTearOff(); + _$LoginStart start() { + return const _$LoginStart(); + } + _$LoginAttempted loginAttempted( {required String userName, required String password, @@ -33,18 +37,16 @@ const $LoginEvent = _$LoginEventTearOff(); /// @nodoc mixin _$LoginEvent { - String get userName => throw _privateConstructorUsedError; - String get password => throw _privateConstructorUsedError; - bool get rememberMe => throw _privateConstructorUsedError; - @optionalTypeArgs TResult when({ + required TResult Function() start, required TResult Function(String userName, String password, bool rememberMe) loginAttempted, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ + TResult Function()? start, TResult Function(String userName, String password, bool rememberMe)? loginAttempted, required TResult orElse(), @@ -52,19 +54,17 @@ mixin _$LoginEvent { throw _privateConstructorUsedError; @optionalTypeArgs TResult map({ + required TResult Function(_$LoginStart value) start, required TResult Function(_$LoginAttempted value) loginAttempted, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeMap({ + TResult Function(_$LoginStart value)? start, TResult Function(_$LoginAttempted value)? loginAttempted, required TResult orElse(), }) => throw _privateConstructorUsedError; - - @JsonKey(ignore: true) - $LoginEventCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc @@ -72,7 +72,6 @@ abstract class $LoginEventCopyWith<$Res> { factory $LoginEventCopyWith( LoginEvent value, $Res Function(LoginEvent) then) = _$LoginEventCopyWithImpl<$Res>; - $Res call({String userName, String password, bool rememberMe}); } /// @nodoc @@ -82,37 +81,100 @@ class _$LoginEventCopyWithImpl<$Res> implements $LoginEventCopyWith<$Res> { final LoginEvent _value; // ignore: unused_field final $Res Function(LoginEvent) _then; +} + +/// @nodoc +abstract class _$$LoginStartCopyWith<$Res> { + factory _$$LoginStartCopyWith( + _$LoginStart value, $Res Function(_$LoginStart) then) = + __$$LoginStartCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$LoginStartCopyWithImpl<$Res> extends _$LoginEventCopyWithImpl<$Res> + implements _$$LoginStartCopyWith<$Res> { + __$$LoginStartCopyWithImpl( + _$LoginStart _value, $Res Function(_$LoginStart) _then) + : super(_value, (v) => _then(v as _$LoginStart)); @override - $Res call({ - Object? userName = freezed, - Object? password = freezed, - Object? rememberMe = freezed, + _$LoginStart get _value => super._value as _$LoginStart; +} + +/// @nodoc + +class _$_$LoginStart implements _$LoginStart { + const _$_$LoginStart(); + + @override + String toString() { + return 'LoginEvent.start()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || (other is _$LoginStart); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() start, + required TResult Function(String userName, String password, bool rememberMe) + loginAttempted, }) { - return _then(_value.copyWith( - userName: userName == freezed - ? _value.userName - : userName // ignore: cast_nullable_to_non_nullable - as String, - password: password == freezed - ? _value.password - : password // ignore: cast_nullable_to_non_nullable - as String, - rememberMe: rememberMe == freezed - ? _value.rememberMe - : rememberMe // ignore: cast_nullable_to_non_nullable - as bool, - )); + return start(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? start, + TResult Function(String userName, String password, bool rememberMe)? + loginAttempted, + required TResult orElse(), + }) { + if (start != null) { + return start(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_$LoginStart value) start, + required TResult Function(_$LoginAttempted value) loginAttempted, + }) { + return start(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_$LoginStart value)? start, + TResult Function(_$LoginAttempted value)? loginAttempted, + required TResult orElse(), + }) { + if (start != null) { + return start(this); + } + return orElse(); } } +abstract class _$LoginStart implements LoginEvent { + const factory _$LoginStart() = _$_$LoginStart; +} + /// @nodoc -abstract class _$$LoginAttemptedCopyWith<$Res> - implements $LoginEventCopyWith<$Res> { +abstract class _$$LoginAttemptedCopyWith<$Res> { factory _$$LoginAttemptedCopyWith( _$LoginAttempted value, $Res Function(_$LoginAttempted) then) = __$$LoginAttemptedCopyWithImpl<$Res>; - @override $Res call({String userName, String password, bool rememberMe}); } @@ -200,6 +262,7 @@ class _$_$LoginAttempted implements _$LoginAttempted { @override @optionalTypeArgs TResult when({ + required TResult Function() start, required TResult Function(String userName, String password, bool rememberMe) loginAttempted, }) { @@ -209,6 +272,7 @@ class _$_$LoginAttempted implements _$LoginAttempted { @override @optionalTypeArgs TResult maybeWhen({ + TResult Function()? start, TResult Function(String userName, String password, bool rememberMe)? loginAttempted, required TResult orElse(), @@ -222,6 +286,7 @@ class _$_$LoginAttempted implements _$LoginAttempted { @override @optionalTypeArgs TResult map({ + required TResult Function(_$LoginStart value) start, required TResult Function(_$LoginAttempted value) loginAttempted, }) { return loginAttempted(this); @@ -230,6 +295,7 @@ class _$_$LoginAttempted implements _$LoginAttempted { @override @optionalTypeArgs TResult maybeMap({ + TResult Function(_$LoginStart value)? start, TResult Function(_$LoginAttempted value)? loginAttempted, required TResult orElse(), }) { @@ -246,13 +312,9 @@ abstract class _$LoginAttempted implements LoginEvent { required String password, required bool rememberMe}) = _$_$LoginAttempted; - @override String get userName => throw _privateConstructorUsedError; - @override String get password => throw _privateConstructorUsedError; - @override bool get rememberMe => throw _privateConstructorUsedError; - @override @JsonKey(ignore: true) _$$LoginAttemptedCopyWith<_$LoginAttempted> get copyWith => throw _privateConstructorUsedError; @@ -266,6 +328,10 @@ class _$LoginStateTearOff { return const _$LoginInitial(); } + _$LoginFirstTime firstTime() { + return const _$LoginFirstTime(); + } + _$LoginAttemptInProgress inProgress() { return const _$LoginAttemptInProgress(); } @@ -289,6 +355,7 @@ mixin _$LoginState { @optionalTypeArgs TResult when({ required TResult Function() initial, + required TResult Function() firstTime, required TResult Function() inProgress, required TResult Function() success, required TResult Function(String error) failure, @@ -297,6 +364,7 @@ mixin _$LoginState { @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, + TResult Function()? firstTime, TResult Function()? inProgress, TResult Function()? success, TResult Function(String error)? failure, @@ -306,6 +374,7 @@ mixin _$LoginState { @optionalTypeArgs TResult map({ required TResult Function(_$LoginInitial value) initial, + required TResult Function(_$LoginFirstTime value) firstTime, required TResult Function(_$LoginAttemptInProgress value) inProgress, required TResult Function(_$LoginAttemptSuccess value) success, required TResult Function(_$LoginAttemptFailure value) failure, @@ -314,6 +383,7 @@ mixin _$LoginState { @optionalTypeArgs TResult maybeMap({ TResult Function(_$LoginInitial value)? initial, + TResult Function(_$LoginFirstTime value)? firstTime, TResult Function(_$LoginAttemptInProgress value)? inProgress, TResult Function(_$LoginAttemptSuccess value)? success, TResult Function(_$LoginAttemptFailure value)? failure, @@ -378,6 +448,7 @@ class _$_$LoginInitial implements _$LoginInitial { @optionalTypeArgs TResult when({ required TResult Function() initial, + required TResult Function() firstTime, required TResult Function() inProgress, required TResult Function() success, required TResult Function(String error) failure, @@ -389,6 +460,7 @@ class _$_$LoginInitial implements _$LoginInitial { @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, + TResult Function()? firstTime, TResult Function()? inProgress, TResult Function()? success, TResult Function(String error)? failure, @@ -404,6 +476,7 @@ class _$_$LoginInitial implements _$LoginInitial { @optionalTypeArgs TResult map({ required TResult Function(_$LoginInitial value) initial, + required TResult Function(_$LoginFirstTime value) firstTime, required TResult Function(_$LoginAttemptInProgress value) inProgress, required TResult Function(_$LoginAttemptSuccess value) success, required TResult Function(_$LoginAttemptFailure value) failure, @@ -415,6 +488,7 @@ class _$_$LoginInitial implements _$LoginInitial { @optionalTypeArgs TResult maybeMap({ TResult Function(_$LoginInitial value)? initial, + TResult Function(_$LoginFirstTime value)? firstTime, TResult Function(_$LoginAttemptInProgress value)? inProgress, TResult Function(_$LoginAttemptSuccess value)? success, TResult Function(_$LoginAttemptFailure value)? failure, @@ -431,6 +505,104 @@ abstract class _$LoginInitial implements LoginState { const factory _$LoginInitial() = _$_$LoginInitial; } +/// @nodoc +abstract class _$$LoginFirstTimeCopyWith<$Res> { + factory _$$LoginFirstTimeCopyWith( + _$LoginFirstTime value, $Res Function(_$LoginFirstTime) then) = + __$$LoginFirstTimeCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$LoginFirstTimeCopyWithImpl<$Res> + extends _$LoginStateCopyWithImpl<$Res> + implements _$$LoginFirstTimeCopyWith<$Res> { + __$$LoginFirstTimeCopyWithImpl( + _$LoginFirstTime _value, $Res Function(_$LoginFirstTime) _then) + : super(_value, (v) => _then(v as _$LoginFirstTime)); + + @override + _$LoginFirstTime get _value => super._value as _$LoginFirstTime; +} + +/// @nodoc + +class _$_$LoginFirstTime implements _$LoginFirstTime { + const _$_$LoginFirstTime(); + + @override + String toString() { + return 'LoginState.firstTime()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || (other is _$LoginFirstTime); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() firstTime, + required TResult Function() inProgress, + required TResult Function() success, + required TResult Function(String error) failure, + }) { + return firstTime(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? firstTime, + TResult Function()? inProgress, + TResult Function()? success, + TResult Function(String error)? failure, + required TResult orElse(), + }) { + if (firstTime != null) { + return firstTime(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_$LoginInitial value) initial, + required TResult Function(_$LoginFirstTime value) firstTime, + required TResult Function(_$LoginAttemptInProgress value) inProgress, + required TResult Function(_$LoginAttemptSuccess value) success, + required TResult Function(_$LoginAttemptFailure value) failure, + }) { + return firstTime(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_$LoginInitial value)? initial, + TResult Function(_$LoginFirstTime value)? firstTime, + TResult Function(_$LoginAttemptInProgress value)? inProgress, + TResult Function(_$LoginAttemptSuccess value)? success, + TResult Function(_$LoginAttemptFailure value)? failure, + required TResult orElse(), + }) { + if (firstTime != null) { + return firstTime(this); + } + return orElse(); + } +} + +abstract class _$LoginFirstTime implements LoginState { + const factory _$LoginFirstTime() = _$_$LoginFirstTime; +} + /// @nodoc abstract class _$$LoginAttemptInProgressCopyWith<$Res> { factory _$$LoginAttemptInProgressCopyWith(_$LoginAttemptInProgress value, @@ -473,6 +645,7 @@ class _$_$LoginAttemptInProgress implements _$LoginAttemptInProgress { @optionalTypeArgs TResult when({ required TResult Function() initial, + required TResult Function() firstTime, required TResult Function() inProgress, required TResult Function() success, required TResult Function(String error) failure, @@ -484,6 +657,7 @@ class _$_$LoginAttemptInProgress implements _$LoginAttemptInProgress { @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, + TResult Function()? firstTime, TResult Function()? inProgress, TResult Function()? success, TResult Function(String error)? failure, @@ -499,6 +673,7 @@ class _$_$LoginAttemptInProgress implements _$LoginAttemptInProgress { @optionalTypeArgs TResult map({ required TResult Function(_$LoginInitial value) initial, + required TResult Function(_$LoginFirstTime value) firstTime, required TResult Function(_$LoginAttemptInProgress value) inProgress, required TResult Function(_$LoginAttemptSuccess value) success, required TResult Function(_$LoginAttemptFailure value) failure, @@ -510,6 +685,7 @@ class _$_$LoginAttemptInProgress implements _$LoginAttemptInProgress { @optionalTypeArgs TResult maybeMap({ TResult Function(_$LoginInitial value)? initial, + TResult Function(_$LoginFirstTime value)? firstTime, TResult Function(_$LoginAttemptInProgress value)? inProgress, TResult Function(_$LoginAttemptSuccess value)? success, TResult Function(_$LoginAttemptFailure value)? failure, @@ -567,6 +743,7 @@ class _$_$LoginAttemptSuccess implements _$LoginAttemptSuccess { @optionalTypeArgs TResult when({ required TResult Function() initial, + required TResult Function() firstTime, required TResult Function() inProgress, required TResult Function() success, required TResult Function(String error) failure, @@ -578,6 +755,7 @@ class _$_$LoginAttemptSuccess implements _$LoginAttemptSuccess { @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, + TResult Function()? firstTime, TResult Function()? inProgress, TResult Function()? success, TResult Function(String error)? failure, @@ -593,6 +771,7 @@ class _$_$LoginAttemptSuccess implements _$LoginAttemptSuccess { @optionalTypeArgs TResult map({ required TResult Function(_$LoginInitial value) initial, + required TResult Function(_$LoginFirstTime value) firstTime, required TResult Function(_$LoginAttemptInProgress value) inProgress, required TResult Function(_$LoginAttemptSuccess value) success, required TResult Function(_$LoginAttemptFailure value) failure, @@ -604,6 +783,7 @@ class _$_$LoginAttemptSuccess implements _$LoginAttemptSuccess { @optionalTypeArgs TResult maybeMap({ TResult Function(_$LoginInitial value)? initial, + TResult Function(_$LoginFirstTime value)? firstTime, TResult Function(_$LoginAttemptInProgress value)? inProgress, TResult Function(_$LoginAttemptSuccess value)? success, TResult Function(_$LoginAttemptFailure value)? failure, @@ -687,6 +867,7 @@ class _$_$LoginAttemptFailure implements _$LoginAttemptFailure { @optionalTypeArgs TResult when({ required TResult Function() initial, + required TResult Function() firstTime, required TResult Function() inProgress, required TResult Function() success, required TResult Function(String error) failure, @@ -698,6 +879,7 @@ class _$_$LoginAttemptFailure implements _$LoginAttemptFailure { @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, + TResult Function()? firstTime, TResult Function()? inProgress, TResult Function()? success, TResult Function(String error)? failure, @@ -713,6 +895,7 @@ class _$_$LoginAttemptFailure implements _$LoginAttemptFailure { @optionalTypeArgs TResult map({ required TResult Function(_$LoginInitial value) initial, + required TResult Function(_$LoginFirstTime value) firstTime, required TResult Function(_$LoginAttemptInProgress value) inProgress, required TResult Function(_$LoginAttemptSuccess value) success, required TResult Function(_$LoginAttemptFailure value) failure, @@ -724,6 +907,7 @@ class _$_$LoginAttemptFailure implements _$LoginAttemptFailure { @optionalTypeArgs TResult maybeMap({ TResult Function(_$LoginInitial value)? initial, + TResult Function(_$LoginFirstTime value)? firstTime, TResult Function(_$LoginAttemptInProgress value)? inProgress, TResult Function(_$LoginAttemptSuccess value)? success, TResult Function(_$LoginAttemptFailure value)? failure, diff --git a/lib/src/presentation/blocs/login_bloc/login_event.dart b/lib/src/presentation/blocs/login_bloc/login_event.dart index 584c378..c5696b2 100644 --- a/lib/src/presentation/blocs/login_bloc/login_event.dart +++ b/lib/src/presentation/blocs/login_bloc/login_event.dart @@ -2,6 +2,7 @@ part of 'login_bloc.dart'; @freezed class LoginEvent with _$LoginEvent { + const factory LoginEvent.start() = _$LoginStart; const factory LoginEvent.loginAttempted({ required String userName, required String password, diff --git a/lib/src/presentation/blocs/login_bloc/login_state.dart b/lib/src/presentation/blocs/login_bloc/login_state.dart index 85b0b68..f4479e1 100644 --- a/lib/src/presentation/blocs/login_bloc/login_state.dart +++ b/lib/src/presentation/blocs/login_bloc/login_state.dart @@ -3,6 +3,7 @@ part of 'login_bloc.dart'; @freezed class LoginState with _$LoginState { const factory LoginState.initial() = _$LoginInitial; + const factory LoginState.firstTime() = _$LoginFirstTime; const factory LoginState.inProgress() = _$LoginAttemptInProgress; const factory LoginState.success() = _$LoginAttemptSuccess; const factory LoginState.failure({required String error}) = diff --git a/lib/src/presentation/blocs/register_bloc/register_bloc.dart b/lib/src/presentation/blocs/register_bloc/register_bloc.dart index a606add..c032016 100644 --- a/lib/src/presentation/blocs/register_bloc/register_bloc.dart +++ b/lib/src/presentation/blocs/register_bloc/register_bloc.dart @@ -6,9 +6,9 @@ import 'package:gestionuh/src/data/models/password_edit_data.dart'; import 'package:gestionuh/src/data/repositories/repositories.dart'; import 'package:gestionuh/src/utils/constants/constants.dart'; +part 'register_bloc.freezed.dart'; part 'register_event.dart'; part 'register_state.dart'; -part 'register_bloc.freezed.dart'; class RegisterBloc extends Bloc { final AuthRepository repository; diff --git a/lib/src/presentation/pages/faqs_page.dart b/lib/src/presentation/pages/faqs_page.dart new file mode 100644 index 0000000..2284368 --- /dev/null +++ b/lib/src/presentation/pages/faqs_page.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:gestionuh/src/utils/constants/constants.dart'; +import 'package:responsive_builder/responsive_builder.dart'; + +class FaqsPage extends StatelessWidget { + const FaqsPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const SelectableText('Preguntas Frecuentes'), + centerTitle: true, + ), + body: Scrollbar( + child: SingleChildScrollView( + child: Center( + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), + width: getValueForScreenType( + context: context, + mobile: MediaQuery.of(context).size.width, + tablet: MediaQuery.of(context).size.width * 0.5, + ), + child: Column( + children: faqs.map((faq) { + return ListTile( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + title: Text(faq.question), + subtitle: Text(faq.answer), + isThreeLine: true, + ); + }).toList(), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/src/presentation/pages/home_page/home_page.dart b/lib/src/presentation/pages/home_page/home_page.dart index 34dad06..8064ff3 100644 --- a/lib/src/presentation/pages/home_page/home_page.dart +++ b/lib/src/presentation/pages/home_page/home_page.dart @@ -270,7 +270,7 @@ class HomePage extends StatelessWidget { return _buildDrawerItem( context: context, text: 'Perfil', - icon: Icons.person, + icon: Icons.person_outline, onTap: () { _applyPopIfDrawerIsDialog(context); context.read().add(HomeEvent.goToProfile(profile)); @@ -290,7 +290,7 @@ class HomePage extends StatelessWidget { return _buildDrawerItem( context: context, text: 'Correo', - icon: Icons.mail, + icon: Icons.mail_outline, onTap: () { _applyPopIfDrawerIsDialog(context); context.read().add(HomeEvent.goToMailQuota(profile)); @@ -300,7 +300,7 @@ class HomePage extends StatelessWidget { return _buildDrawerItem( context: context, text: 'Cambiar Contraseña', - icon: Icons.security_rounded, + icon: Icons.security_outlined, onTap: () { _applyPopIfDrawerIsDialog(context); context.read().add(HomeEvent.goToResetPassword(profile)); @@ -320,13 +320,13 @@ class HomePage extends StatelessWidget { content: const SelectableText( '¿Está seguro que desea cerrar sesión?'), actions: [ - TextButton( + ElevatedButton( onPressed: () { Navigator.of(context).pop(true); }, child: const Text('Si'), ), - TextButton( + ElevatedButton( onPressed: () async { Navigator.of(context).pop(false); }, @@ -361,6 +361,16 @@ class HomePage extends StatelessWidget { context.read().add(HomeEvent.goToHelpfulLinks(profile)); }, ); + case HomeItemEnum.Faqs: + return _buildDrawerItem( + context: context, + text: 'Preguntas Frecuentes', + icon: Icons.help_outline, + onTap: () { + _applyPopIfDrawerIsDialog(context); + Navigator.of(context).pushNamed(FAQS_ROUTE_NAME); + }, + ); } } diff --git a/lib/src/presentation/pages/home_page/sub_pages/about_page.dart b/lib/src/presentation/pages/home_page/sub_pages/about_page.dart index 4525349..dd68318 100644 --- a/lib/src/presentation/pages/home_page/sub_pages/about_page.dart +++ b/lib/src/presentation/pages/home_page/sub_pages/about_page.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:gestionuh/src/presentation/widgets/widgets.dart'; import 'package:gestionuh/src/utils/constants/constants.dart'; +import 'package:gestionuh/src/utils/open_url.dart'; import 'package:responsive_builder/responsive_builder.dart'; -import 'package:url_launcher/url_launcher.dart'; class AboutInformationPage extends StatelessWidget { const AboutInformationPage({Key? key}) : super(key: key); @@ -78,15 +77,7 @@ class AboutInformationPage extends StatelessWidget { onTap: e.link == null ? null : () async { - if (await canLaunch(e.link!)) { - await launch(e.link!); - } else { - FlashHelper.errorBar( - context, - message: - 'No puede acceder a ${e.link}', - ); - } + await openUrl(context, e.link!); }, trailing: Icon(e.link != null ? Icons.link @@ -117,14 +108,7 @@ class AboutInformationPage extends StatelessWidget { onTap: () async { const url = 'https://github.com/NODO-UH/gestionapp/blob/master/LICENSE'; - if (await canLaunch(url)) { - await launch(url); - } else { - FlashHelper.errorBar( - context, - message: 'No puede acceder a $url', - ); - } + await openUrl(context, url); }, child: Container( padding: const EdgeInsets.symmetric( diff --git a/lib/src/presentation/pages/home_page/sub_pages/helpful_links_page.dart b/lib/src/presentation/pages/home_page/sub_pages/helpful_links_page.dart index feab0ed..d3fcbaa 100644 --- a/lib/src/presentation/pages/home_page/sub_pages/helpful_links_page.dart +++ b/lib/src/presentation/pages/home_page/sub_pages/helpful_links_page.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:gestionuh/src/presentation/widgets/widgets.dart'; import 'package:gestionuh/src/utils/constants/constants.dart'; +import 'package:gestionuh/src/utils/open_url.dart'; import 'package:responsive_builder/responsive_builder.dart'; -import 'package:url_launcher/url_launcher.dart'; class HelpfulLinksPage extends StatelessWidget { const HelpfulLinksPage({Key? key}) : super(key: key); @@ -29,14 +28,7 @@ class HelpfulLinksPage extends StatelessWidget { subtitle: Text(helpfulLink.url), trailing: const Icon(Icons.link), onTap: () async { - if (await canLaunch(helpfulLink.url)) { - await launch(helpfulLink.url); - } else { - FlashHelper.errorBar( - context, - message: 'No puede acceder a ${helpfulLink.url}', - ); - } + await openUrl(context, helpfulLink.url); }, ); }).toList(), diff --git a/lib/src/presentation/pages/login_page.dart b/lib/src/presentation/pages/login_page.dart index 328f719..453889a 100644 --- a/lib/src/presentation/pages/login_page.dart +++ b/lib/src/presentation/pages/login_page.dart @@ -57,6 +57,14 @@ class _LoginPageState extends State { appBar: AppBar( title: const SelectableText(Constants.appName), centerTitle: true, + actions: [ + IconButton( + icon: const Icon(Icons.help), + onPressed: () { + Navigator.of(context).pushNamed(FAQS_ROUTE_NAME); + }, + ), + ], ), bottomSheet: const GestionUHBottomSheet(), body: BlocConsumer( @@ -65,15 +73,38 @@ class _LoginPageState extends State { Navigator.of(context).pushReplacementNamed(HOME_ROUTE_NAME); } state.maybeWhen( + firstTime: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('¡Bienvenido a ${Constants.appName}!'), + content: const Scrollbar( + child: SelectableText(INITIAL_MESSAGE), + ), + actions: [ + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Entendido'), + ), + ], + ); + }, + ); + }, failure: (error) => FlashHelper.errorBar(context, message: error), orElse: () {}, ); }, builder: (context, state) { - return state.maybeWhen( + return state.when( inProgress: () => _buildLoadingIndicator(context), success: () => _buildLoadingIndicator(context), - orElse: () => _buildLoginForm(context), + failure: (String error) => _buildLoginForm(context), + firstTime: () => _buildLoginForm(context), + initial: () => _buildLoginForm(context), ); }, ), @@ -103,14 +134,11 @@ class _LoginPageState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - const SizedBox( - height: 10, - ), Image.asset( 'assets/images/splash.png', ), const SizedBox( - height: 40, + height: 20, ), SizedBox( child: Row( @@ -205,7 +233,7 @@ class _LoginPageState extends State { onPressed: () => _registerAction(context), ), const SizedBox( - height: 30, + height: 50, ), ], ), diff --git a/lib/src/presentation/pages/pages.dart b/lib/src/presentation/pages/pages.dart index 4221e0d..c8a3502 100644 --- a/lib/src/presentation/pages/pages.dart +++ b/lib/src/presentation/pages/pages.dart @@ -1,3 +1,4 @@ +export 'faqs_page.dart'; export 'home_page/home_page.dart'; export 'login_page.dart'; export 'recover_password_page.dart'; diff --git a/lib/src/presentation/pages/recover_password_page.dart b/lib/src/presentation/pages/recover_password_page.dart index fb005fc..94c2659 100644 --- a/lib/src/presentation/pages/recover_password_page.dart +++ b/lib/src/presentation/pages/recover_password_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gestionuh/src/presentation/blocs/blocs.dart'; import 'package:gestionuh/src/presentation/widgets/widgets.dart'; +import 'package:gestionuh/src/utils/constants/constants.dart'; import 'package:gestionuh/src/utils/validators.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -15,6 +16,14 @@ class RecoverPasswordPage extends StatelessWidget { appBar: AppBar( title: const SelectableText('Recuperar Contraseña'), centerTitle: true, + actions: [ + IconButton( + icon: const Icon(Icons.help), + onPressed: () { + Navigator.of(context).pushNamed(FAQS_ROUTE_NAME); + }, + ), + ], ), bottomSheet: const GestionUHBottomSheet(), body: Scrollbar( @@ -284,7 +293,7 @@ class RecoverPasswordPage extends StatelessWidget { const SizedBox(height: 30), GestionUhDefaultButton( onPressed: () => Navigator.of(context).pop(), - child: const Text('Ok'), + child: const Text('Entendido'), ), ], ), diff --git a/lib/src/presentation/pages/register_page.dart b/lib/src/presentation/pages/register_page.dart index 683160f..71d54f6 100644 --- a/lib/src/presentation/pages/register_page.dart +++ b/lib/src/presentation/pages/register_page.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gestionuh/src/presentation/blocs/blocs.dart'; import 'package:gestionuh/src/presentation/widgets/widgets.dart'; import 'package:gestionuh/src/utils/constants/constants.dart'; +import 'package:gestionuh/src/utils/open_url.dart'; import 'package:gestionuh/src/utils/pair.dart'; import 'package:gestionuh/src/utils/validators.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -98,6 +99,14 @@ class _RegisterPageState extends State { appBar: AppBar( title: const SelectableText('Registrarse'), centerTitle: true, + actions: [ + IconButton( + icon: const Icon(Icons.help), + onPressed: () { + Navigator.of(context).pushNamed(FAQS_ROUTE_NAME); + }, + ), + ], ), body: BlocConsumer( listener: (context, state) async { @@ -279,51 +288,85 @@ class _RegisterPageState extends State { } Widget _buildRegisterSuccessPage(BuildContext context, String userEmail) { - return Container( - margin: const EdgeInsets.all(30), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SelectableText.rich( - TextSpan( - style: Theme.of(context).textTheme.subtitle1, + return Scrollbar( + child: SingleChildScrollView( + child: Center( + child: Container( + width: getValueForScreenType( + context: context, + mobile: MediaQuery.of(context).size.width, + tablet: MediaQuery.of(context).size.width * 0.5, + ), + padding: const EdgeInsets.only( + top: 30, + bottom: 9, + left: 18, + right: 18, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - const TextSpan( - text: 'Se ha registrado correctamente ' - 'su correo es ', + SelectableText.rich( + TextSpan( + style: Theme.of(context).textTheme.subtitle1, + children: [ + const TextSpan( + text: 'Se ha registrado correctamente, ' + 'su correo es ', + ), + TextSpan( + text: '"$userEmail"', + style: Theme.of(context) + .textTheme + .subtitle1 + ?.copyWith(color: Colors.red), + ), + const TextSpan( + text: ', anótelo de ser necesario ' + 'no se mostrará otra vez.', + ), + ], + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 15), + GestionUhDefaultButton( + onPressed: () { + Clipboard.setData(ClipboardData(text: userEmail)); + FlashHelper.successBar( + context, + message: 'Correo copiado correctamente.', + ); + }, + child: const Text('Copiar Correo'), + ), + const SizedBox(height: 15), + SelectableText( + 'Se le ha enviado un correo electrónico, a continuación ' + 'vaya a la bandeja de correos para recibarlo.', + style: Theme.of(context).textTheme.subtitle1, + textAlign: TextAlign.center, + ), + const SizedBox(height: 15), + GestionUhDefaultButton( + onPressed: () => + openUrl(context, 'https://correo.estudiantes.uh.cu'), + child: const Text('Ir al correo para "Estudiantes"'), ), - TextSpan( - text: '"$userEmail"', - style: Theme.of(context) - .textTheme - .subtitle1 - ?.copyWith(color: Colors.red), + const SizedBox(height: 10), + GestionUhDefaultButton( + onPressed: () => openUrl(context, 'https://correo.uh.cu'), + child: const Text('Ir al correo para "Profesores"'), ), - const TextSpan( - text: ', anótelo de ser necesario ' - 'no se mostrará otra vez.', + const SizedBox(height: 30), + GestionUhDefaultButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Entendido'), ), ], ), - textAlign: TextAlign.center, ), - const SizedBox(height: 15), - GestionUhDefaultButton( - onPressed: () { - Clipboard.setData(ClipboardData(text: userEmail)); - FlashHelper.successBar( - context, - message: 'Correo copiado correctamente.', - ); - }, - child: const Text('Copiar Correo'), - ), - const SizedBox(height: 30), - GestionUhDefaultButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('Ok'), - ), - ], + ), ), ); } diff --git a/lib/src/router.dart b/lib/src/router.dart index 18f975a..ea1f45c 100644 --- a/lib/src/router.dart +++ b/lib/src/router.dart @@ -14,7 +14,7 @@ class RouterNavigation { final authRepo = GetIt.I(); if (authRepo.logged) { return _buildPage( - builder: BlocProvider( + child: BlocProvider( create: (_) => GetIt.I()..add(const HomeEvent.loadProfile()), child: HomePage(), ), @@ -22,15 +22,15 @@ class RouterNavigation { ); } return _buildPage( - builder: BlocProvider( - create: (_) => GetIt.I(), + child: BlocProvider( + create: (_) => GetIt.I()..add(const LoginEvent.start()), child: const LoginPage(), ), settings: settings, ); case REGISTER_ROUTE_NAME: return _buildPage( - builder: Overlay( + child: Overlay( initialEntries: [ OverlayEntry(builder: (context) { return BlocProvider( @@ -45,18 +45,23 @@ class RouterNavigation { ); case RECOVER_PASSWORD_ROUTE_NAME: return _buildPage( - builder: BlocProvider( + child: BlocProvider( create: (_) => GetIt.I(), child: RecoverPasswordPage(), ), settings: settings, ); + case FAQS_ROUTE_NAME: + return _buildPage( + child: const FaqsPage(), + settings: settings, + ); case HOME_ROUTE_NAME: default: final authRepo = GetIt.I(); if (authRepo.logged) { return _buildPage( - builder: BlocProvider( + child: BlocProvider( create: (_) => GetIt.I()..add(const HomeEvent.loadProfile()), child: HomePage(), ), @@ -64,8 +69,8 @@ class RouterNavigation { ); } return _buildPage( - builder: BlocProvider( - create: (_) => GetIt.I(), + child: BlocProvider( + create: (_) => GetIt.I()..add(const LoginEvent.start()), child: const LoginPage(), ), settings: settings, @@ -74,11 +79,11 @@ class RouterNavigation { } static PageRoute _buildPage({ - required Widget builder, + required Widget child, required RouteSettings settings, }) { return PageTransition( - child: builder, + child: child, settings: settings, type: PageTransitionType.fade, ); diff --git a/lib/src/utils/constants/constants.dart b/lib/src/utils/constants/constants.dart index 0ec0485..fb0473b 100644 --- a/lib/src/utils/constants/constants.dart +++ b/lib/src/utils/constants/constants.dart @@ -1,5 +1,6 @@ export 'about.dart'; export 'errors.dart'; +export 'faqs.dart'; export 'helpful_links.dart'; export 'messages.dart'; export 'misc.dart'; diff --git a/lib/src/utils/constants/faqs.dart b/lib/src/utils/constants/faqs.dart new file mode 100644 index 0000000..fb93041 --- /dev/null +++ b/lib/src/utils/constants/faqs.dart @@ -0,0 +1,22 @@ +const faqs = [ + Faq( + question: + '¿Qué hacer en caso de confrontar alguna dificultad en el momento de la matrícula?', + answer: + 'Debe comunicarse con la Universidad de La Habana, a través de los teléfonos ' + '78734251 y 78788115, en días laborables, entre las 9:00AM y 1:00PM.', + ), + Faq( + question: '¿Por qué da error cuando intento recuperar la contraseña?', + answer: + 'El proceso de recuperar la contraseña solo esta disponible para los usuarios nuevos. ' + 'Para los antiguos usuarios se encuentra en desarrollo aún.', + ), +]; + +class Faq { + final String question; + final String answer; + + const Faq({required this.question, required this.answer}); +} diff --git a/lib/src/utils/constants/messages.dart b/lib/src/utils/constants/messages.dart index 7908e69..175f058 100644 --- a/lib/src/utils/constants/messages.dart +++ b/lib/src/utils/constants/messages.dart @@ -29,3 +29,13 @@ const TERMS_AND_CONDITIONS_OF_USE_ENUM_TERMS = '\nLas cuentas asignadas a los usuarios serán auditadas con vistas a verificar el cumplimiento de la ética. ' '\n' '\nAcepto cumplir con todo lo antes expuesto.'; + +const INITIAL_MESSAGE = + 'En caso de confrontar alguna dificultad en el momento de la matrícula debe ' + 'comunicarse con la Universidad de La Habana, a través de los teléfonos 78734251 ' + 'y 78788115, en días laborables, entre las 9:00AM y 1:00PM.\n\n' + 'El proceso de recuperar la contraseña solo esta disponible para los usuarios nuevos. ' + 'Para los antiguos usuarios se encuentra en desarrollo aún.\n\n' + 'Puede volver a leer estos mensajes en la sección de "Preguntas Frecuentes" ' + 'accesible desde el menú lateral o desde el botón de ayuda en la esquina superior ' + 'derecha en los casos donde no haya un menú lateral.'; diff --git a/lib/src/utils/constants/routes.dart b/lib/src/utils/constants/routes.dart index e5f5bae..c0fe4e4 100644 --- a/lib/src/utils/constants/routes.dart +++ b/lib/src/utils/constants/routes.dart @@ -2,6 +2,7 @@ const LOGIN_ROUTE_NAME = '/login'; const REGISTER_ROUTE_NAME = '/register'; const HOME_ROUTE_NAME = '/'; const RECOVER_PASSWORD_ROUTE_NAME = '/recover-password'; +const FAQS_ROUTE_NAME = '/faqs'; // const QUOTA_ROUTE_NAME = '/quota'; // const PROFILE_ROUTE_NAME = '/profile'; // const MAIL_ROUTE_NAME = '/mail'; diff --git a/lib/src/utils/constants/storage_keys.dart b/lib/src/utils/constants/storage_keys.dart index a5db0c2..7b65144 100644 --- a/lib/src/utils/constants/storage_keys.dart +++ b/lib/src/utils/constants/storage_keys.dart @@ -4,3 +4,4 @@ const USER_NAME = '${STORAGE_PREFIX}username'; const USER_PASSWORD = '${STORAGE_PREFIX}password'; const USER_LOGGED_INTO = '${STORAGE_PREFIX}logged.into'; const USER_REMEMBERME = '${STORAGE_PREFIX}rememberme'; +const APP_FIRST_TIME = '${STORAGE_PREFIX}first.time'; diff --git a/lib/src/utils/open_url.dart b/lib/src/utils/open_url.dart new file mode 100644 index 0000000..f735b9a --- /dev/null +++ b/lib/src/utils/open_url.dart @@ -0,0 +1,14 @@ +import 'package:flutter/cupertino.dart'; +import 'package:gestionuh/src/presentation/widgets/widgets.dart'; +import 'package:url_launcher/url_launcher.dart'; + +Future openUrl(BuildContext context, String url) async { + if (await canLaunch(url)) { + await launch(url); + } else { + FlashHelper.errorBar( + context, + message: 'No puede acceder a $url', + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 0044cc0..3f830ca 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Aplicación para la gestión electrónica en la Universidad de La H publish_to: 'none' -version: 1.0.2+3 +version: 1.1.0+4 environment: sdk: '>=2.12.0 <3.0.0'