Skip to content

Commit

Permalink
Merge pull request #314 from sahani-deriv/auth-ui-update
Browse files Browse the repository at this point in the history
feat: update_auth_ui
  • Loading branch information
ahrar-deriv authored Nov 20, 2023
2 parents 5e6ddfb + f48c322 commit b2c282e
Show file tree
Hide file tree
Showing 59 changed files with 1,962 additions and 440 deletions.
132 changes: 132 additions & 0 deletions packages/deriv_auth_ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,63 @@ MaterialApp(
)
```

## AuthErrorHandler

Since `DerivAuthCubit` influences many features like - login, signup, change password, etc. To be ease the error handling by making it needed to implement in only one place and to make sure all the auth error cases has been handled we have created a base class which client app can extend if they want changes in the default error handling.

```dart
base class AuthErrorStateHandler {
/// {@macro default_auth_error_state_handler}
AuthErrorStateHandler({
required this.context,
});
/// The [BuildContext] of the widget that is using this handler.
final BuildContext context;
/// On invalid 2FA code.
void invalid2faCode(DerivAuthErrorState state) {
showErrorDialog(
context: context,
errorMessage: context.localization.informInvalid2FACode,
actionLabel: context.localization.actionTryAgain,
);
}
/// On invalid email or password.
void invalidEmailOrPassword(DerivAuthErrorState state) {
//....
}
// ...
}
```
If client app wants to customize the error handling they can extend the `AuthErrorStateHandler` and override the methods they want to customize.

```dart
final class CustomAuthErrorStateHandler extends AuthErrorStateHandler {
CustomAuthErrorStateHandler({
required BuildContext context,
}) : super(context: context);
@override
void invalid2faCode(DerivAuthErrorState state) {
//...
}
}
```
The client app can pass the custom error handler to the layout's constructor.

```dart
DerivLoginLayout(
// ...
authErrorStateHandler: CustomAuthErrorStateHandler(context: context),
// ...
)
```
The package handles the mapping of the error state to the corresponding method in the `AuthErrorStateHandler` class within the layout.

## Layouts provided:

### - Get Started Flow
Expand All @@ -60,6 +117,9 @@ MaterialApp(
backgroundImagePath: backgroundImagePath,
onLoginTapped: () {},
onSignupTapped: () {},
onTapNavigation: (){
// Callback to be called when pressed on the screen seven times
},
);
```
### - Login Flow
Expand All @@ -69,6 +129,7 @@ MaterialApp(
welcomeLabel: 'Welcome back!',
greetingLabel:
'Log in to your Deriv account to start trading and investing.',
authErrorStateHandler: AuthErrorStateHandler(context: context),
onResetPassTapped: () {
// Navigate to reset password page
},
Expand Down Expand Up @@ -99,6 +160,7 @@ MaterialApp(
``` dart
DerivSignupLayout(
signupPageLabel: 'Start trading with Deriv',
authErrorStateHandler: AuthErrorStateHandler(context: context),
signupPageDescription:
'Join over 1 million traders worldwide who loves trading at Deriv.',
onSocialAuthButtonPressed: (SocialAuthProvider provider) {},
Expand Down Expand Up @@ -140,6 +202,7 @@ MaterialApp(
``` dart
DerivSetPasswordLayout(
onDerivAuthState: (BuildContext, DerivAuthState) {},
authErrorStateHandler: AuthErrorStateHandler(context: context),
onDerivSignupState: (BuildContext, DerivSignupState) {},
onPreviousPressed: () {},
verificationCode: '123456',
Expand All @@ -164,3 +227,72 @@ MaterialApp(
),
```
- **Reset Password Success Layout**
``` dart
DerivSuccessPassChangeLayout();
```
## Additional:
### AuthListener
`AuthListener` is a widget that listens to the `DerivAuthCubit` state and calls the corresponding callback. This widget is created for ease of using `AuthErrorStateHandler` by handling the mapping of the error state to the corresponding method in the `AuthErrorStateHandler` class.
```dart
class DerivAuthStateListener extends StatelessWidget {
/// {@macro auth_state_listener}
const DerivAuthStateListener({
required this.child,
super.key,
this.onLoggedIn,
this.onLoggedOut,
this.onLoading,
this.onError,
this.authErrorStateHandler,
});
/// The [Widget] that is using this [DerivAuthStateListener].
final Widget child;
/// Callback to be called when user is logged in.
final Function(DerivAuthLoggedInState)? onLoggedIn;
/// Callback to be called when user is logged out.
final VoidCallback? onLoggedOut;
/// Callback to be called when user is logging in.
final VoidCallback? onLoading;
/// Callback to be called when an error occurs.
final Function(DerivAuthErrorState)? onError;
/// Extension of base [AuthErrorStateHandler]. If not provided, base implementation will be used.
final AuthErrorStateHandler? authErrorStateHandler;
@override
Widget build(BuildContext context) =>
BlocListener<DerivAuthCubit, DerivAuthState>(
listener: (BuildContext context, DerivAuthState state) {
if (state is DerivAuthLoggedInState) {
onLoggedIn?.call(state);
} else if (state is DerivAuthLoggedOutState) {
onLoggedOut?.call();
} else if (state is DerivAuthLoadingState) {
onLoading?.call();
} else if (state is DerivAuthErrorState) {
onError?.call(state);
authErrorStateMapper(
authErrorState: state,
authErrorStateHandler: authErrorStateHandler ??
AuthErrorStateHandler(context: context),
);
}
},
child: child,
);
}
```
4 changes: 1 addition & 3 deletions packages/deriv_auth_ui/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,10 @@ linter:
- flutter_style_todos
- hash_and_equals
- implementation_imports
- iterable_contains_unrelated_type
- collection_methods_unrelated_type
- join_return_with_assignment
- library_names
- library_prefixes
# - lines_longer_than_80_chars
- list_remove_unrelated_type
- no_adjacent_strings_in_list
- no_duplicate_case_values
- no_leading_underscores_for_local_identifiers
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:deriv_auth/deriv_auth.dart';
import 'package:deriv_auth_ui/deriv_auth_ui.dart';
import 'package:flutter/material.dart';

final class ExampleAuthErrorStateHandler extends AuthErrorStateHandler {
ExampleAuthErrorStateHandler({required super.context});

@override
void onInvalidCredentials(DerivAuthErrorState state) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ class GetStartedPage extends StatelessWidget {
builder: (context) => const SignupPage(),
),
),
onTapNavigation: () {},
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:deriv_auth/features/auth/cubit/deriv_auth_cubit.dart';
import 'package:deriv_auth_ui/deriv_auth_ui.dart';
import 'package:example/core/example_auth_error_state_handler.dart';
import 'package:example/features/home/pages/home_page.dart';
import 'package:example/features/reset_pass/pages/reset_pass_page.dart';
import 'package:example/features/signup/pages/signup_page.dart';
Expand Down Expand Up @@ -32,6 +33,7 @@ class _LoginPageState extends State<LoginPage> {
builder: (context) => const HomePage(),
),
),
authErrorStateHandler: ExampleAuthErrorStateHandler(context: context),
onLoginError: (_) {},
onResetPassTapped: () => Navigator.push(
context,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:deriv_auth/features/reset_password/cubit/reset_password_cubit.dart';
import 'package:deriv_auth_ui/deriv_auth_ui.dart';
import 'package:example/features/get_started/pages/get_started_page.dart';
import 'package:example/features/reset_pass/pages/reset_pass_success_page.dart';
import 'package:flutter/material.dart';

class ChooseNewPasswordPage extends StatelessWidget {
Expand All @@ -13,11 +13,10 @@ class ChooseNewPasswordPage extends StatelessWidget {
return DerivChooseNewPassLayout(
token: 'token',
onResetPassSucceed: () {
Navigator.of(context).pushAndRemoveUntil(
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const GetStartedPage(),
builder: (context) => const ResetPassSuccessPage(),
),
(Route<dynamic> route) => false,
);
},
onResetPassError: (_) {},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:deriv_auth/deriv_auth.dart';
import 'package:deriv_auth_ui/deriv_auth_ui.dart';
import 'package:example/features/get_started/pages/get_started_page.dart';
import 'package:example/features/login/pages/login_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

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

@override
State<ResetPassSuccessPage> createState() => _ResetPassSuccessPageState();
}

class _ResetPassSuccessPageState extends State<ResetPassSuccessPage> {
static const Duration _successPageHoldDuration = Duration(seconds: 2);

@override
void initState() {
super.initState();

// wait for either [_successPageHoldDuration] or logout to finish
// then navigate to loginPage
Future.wait<void>(
<Future<void>>[
Future<void>.delayed(_successPageHoldDuration),
BlocProvider.of<DerivAuthCubit>(context).logout(),
],
).then(
(_) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => const GetStartedPage(),
),
(route) => false,
);

Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const LoginPage(),
),
);
},
);
}

@override
Widget build(BuildContext context) {
return const DerivSuccessPassChangeLayout();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:deriv_auth/deriv_auth.dart';
import 'package:deriv_auth_ui/deriv_auth_ui.dart';
import 'package:example/features/home/pages/home_page.dart';
import 'package:example/core/example_auth_error_state_handler.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

Expand All @@ -12,16 +12,7 @@ class SetPasswordPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DerivSetPasswordLayout(
onDerivAuthState: (context, state) {
if (state is DerivAuthLoggedInState) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => const HomePage(),
),
(Route<dynamic> route) => false,
);
}
},
authErrorStateHandler: ExampleAuthErrorStateHandler(context: context),
onDerivSignupState: (context, state) {
if (state is DerivSignupDoneState) {
context
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:deriv_auth_ui/deriv_auth_ui.dart';
import 'package:example/core/example_auth_error_state_handler.dart';
import 'package:example/features/login/pages/login_page.dart';
import 'package:example/features/signup/pages/verify_email_page.dart';
import 'package:flutter/material.dart';
Expand All @@ -13,6 +14,7 @@ class SignupPage extends StatefulWidget {
class _SignupPageState extends State<SignupPage> {
@override
Widget build(BuildContext context) => DerivSignupLayout(
authErrorStateHandler: ExampleAuthErrorStateHandler(context: context),
signupPageLabel: 'Start trading with Deriv',
signupPageDescription:
'Join over 1 million traders worldwide who loves trading at Deriv.',
Expand Down
14 changes: 7 additions & 7 deletions packages/deriv_auth_ui/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ dependencies:
path: ../
deriv_auth:
git:
url: [email protected]:regentmarkets/flutter-deriv-packages.git
url: [email protected]:sahani-deriv/flutter-deriv-packages.git
path: packages/deriv_auth
ref: dev
ref: auth-ui-update

deriv_theme:
path: ../../deriv_theme
# git:
# url: [email protected]:regentmarkets/flutter-deriv-packages.git
# path: packages/deriv_theme
# ref: dev
git:
url: [email protected]:regentmarkets/flutter-deriv-packages.git
path: packages/deriv_theme
ref: dev
flutter_bloc: ^8.1.3
http: ^0.13.6

Expand Down
2 changes: 2 additions & 0 deletions packages/deriv_auth_ui/lib/deriv_auth_ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export 'src/features/login/layouts/deriv_2fa_layout.dart';
export 'src/features/login/layouts/deriv_login_layout.dart';
export 'src/features/reset_pass/layouts/deriv_choose_new_pass_layout.dart';
export 'src/features/reset_pass/layouts/deriv_reset_pass_layout.dart';
export 'src/features/reset_pass/layouts/deriv_success_pass_change_layout.dart';
export 'src/features/signup/cubits/deriv_country_selection_cubit.dart';
export 'src/features/signup/layouts/deriv_country_selection_layout.dart';
export 'src/features/signup/layouts/deriv_email_not_received_layout.dart';
Expand All @@ -15,3 +16,4 @@ export 'src/features/signup/layouts/deriv_verify_email_layout.dart';
export 'src/features/signup/models/deriv_auth_utm_model.dart';
export 'src/features/signup/models/deriv_password_policy_model.dart';
export 'src/features/signup/models/deriv_residence_model.dart';
export 'src/core/states/states.dart';
Loading

0 comments on commit b2c282e

Please sign in to comment.