From 2c4e503a3e064184767f5865abaffc021016e95a Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:55:50 +0800 Subject: [PATCH 01/44] deps: update deriv_auth_ui dependencies --- packages/deriv_auth_ui/example/pubspec.yaml | 11 +++---- packages/deriv_auth_ui/pubspec.yaml | 13 ++++---- ...deriv_unavailable_country_layout_test.dart | 4 +-- .../deriv_get_started_layout_test.dart | 9 ++--- .../login/layouts/deriv_2fa_layout_test.dart | 15 +++++---- .../layouts/deriv_login_layout_test.dart | 33 +++++++++++-------- .../deriv_choose_new_pass_layout_test.dart | 12 +++---- .../layouts/deriv_reset_pass_layout_test.dart | 16 +++++---- .../deriv_country_selection_layout_test.dart | 7 ++-- .../deriv_email_not_received_layout_test.dart | 7 ++-- .../deriv_set_password_layout_test.dart | 16 +++++---- .../layouts/deriv_signup_layout_test.dart | 14 ++++---- .../deriv_verification_done_layout_test.dart | 6 ++-- .../deriv_verify_email_layout_test.dart | 7 ++-- .../models/deriv_auth_utm_model_test.dart | 9 ++--- .../deriv_password_policy_model_test.dart | 13 +++++--- .../models/deriv_residence_model_test.dart | 4 +-- .../country_selection_list_widget_test.dart | 11 ++++--- packages/deriv_auth_ui/test/pump_app.dart | 2 +- 19 files changed, 113 insertions(+), 96 deletions(-) diff --git a/packages/deriv_auth_ui/example/pubspec.yaml b/packages/deriv_auth_ui/example/pubspec.yaml index d72faeb0d..f21400657 100644 --- a/packages/deriv_auth_ui/example/pubspec.yaml +++ b/packages/deriv_auth_ui/example/pubspec.yaml @@ -18,13 +18,12 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_auth - ref: d5bef178341a318f911b42b8105d8c3d50c6b63e + ref: dev deriv_theme: - path: ../../deriv_theme - # git: - # url: git@github.com:regentmarkets/flutter-deriv-packages.git - # path: packages/deriv_theme - # ref: dev + git: + url: git@github.com:regentmarkets/flutter-deriv-packages.git + path: packages/deriv_theme + ref: dev flutter_bloc: ^8.1.3 http: ^0.13.6 diff --git a/packages/deriv_auth_ui/pubspec.yaml b/packages/deriv_auth_ui/pubspec.yaml index 0b7c361f6..ee846f0cf 100644 --- a/packages/deriv_auth_ui/pubspec.yaml +++ b/packages/deriv_auth_ui/pubspec.yaml @@ -18,13 +18,12 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_auth - ref: d5bef178341a318f911b42b8105d8c3d50c6b63e + ref: dev deriv_theme: - path: ../deriv_theme - # git: - # url: git@github.com:regentmarkets/flutter-deriv-packages.git - # path: packages/deriv_theme - # ref: dev + git: + url: git@github.com:regentmarkets/flutter-deriv-packages.git + path: packages/deriv_theme + ref: dev deriv_ui: path: ../deriv_ui flutter_svg: ^1.1.6 @@ -40,7 +39,7 @@ dev_dependencies: sdk: flutter flutter_lints: ^2.0.0 mocktail: ^0.3.0 - patrol: ^1.1.4 + patrol_finders: ^1.0.0 bloc_test: ^9.1.3 flutter_intl: diff --git a/packages/deriv_auth_ui/test/core/layouts/deriv_unavailable_country_layout_test.dart b/packages/deriv_auth_ui/test/core/layouts/deriv_unavailable_country_layout_test.dart index ed7d7feda..4a401d9e3 100644 --- a/packages/deriv_auth_ui/test/core/layouts/deriv_unavailable_country_layout_test.dart +++ b/packages/deriv_auth_ui/test/core/layouts/deriv_unavailable_country_layout_test.dart @@ -1,12 +1,12 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_ui/presentation/widgets/fullscreen_message.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../base_test_app.dart'; void main() { - patrolTest('DerivUnavailableCountryLayout', (PatrolTester $) async { + patrolWidgetTest('DerivUnavailableCountryLayout', (PatrolTester $) async { await $.pumpWidgetAndSettle(BaseTestApp( child: DerivUnavailableCountryLayout( userAgent: '', diff --git a/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart b/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart index 82100761b..fecfca5db 100644 --- a/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart @@ -5,7 +5,7 @@ import 'package:deriv_ui/deriv_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; @@ -26,7 +26,8 @@ void main() { when(() => mockSlideModel.supportingText).thenReturn('Supporting text'); }); - patrolTest('should render DerivGetStartedLayout', (PatrolTester $) async { + patrolWidgetTest('should render DerivGetStartedLayout', + (PatrolTester $) async { await $.pumpApp(DerivGetStartedLayout( slides: [mockSlideModel], appLogoIconPath: appLogoIconPath, @@ -41,7 +42,7 @@ void main() { expect($(SecondaryButton), findsOneWidget); }); - patrolTest('should call onLoginTapped when login button is pressed', + patrolWidgetTest('should call onLoginTapped when login button is pressed', (PatrolTester $) async { bool loginTapped = false; @@ -60,7 +61,7 @@ void main() { expect(loginTapped, isTrue); }); - patrolTest('should call onSignupTapped when signup button is pressed', + patrolWidgetTest('should call onSignupTapped when signup button is pressed', (PatrolTester $) async { bool signupTapped = false; diff --git a/packages/deriv_auth_ui/test/features/login/layouts/deriv_2fa_layout_test.dart b/packages/deriv_auth_ui/test/features/login/layouts/deriv_2fa_layout_test.dart index a835bd124..16d1f29a5 100644 --- a/packages/deriv_auth_ui/test/features/login/layouts/deriv_2fa_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/login/layouts/deriv_2fa_layout_test.dart @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -25,7 +25,7 @@ void main() { mockPassword = 'test1234'; }); - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { when(() => authCubit.state).thenAnswer((_) => DerivAuthLoggedOutState()); when(() => authCubit.stream) @@ -44,7 +44,8 @@ void main() { expect($(ElevatedButton).$('Proceed'), findsOneWidget); }); - patrolTest('proceeds to login on correct code', (PatrolTester $) async { + patrolWidgetTest('proceeds to login on correct code', + (PatrolTester $) async { when(() => authCubit.state).thenAnswer((_) => DerivAuthLoggedOutState()); when(() => authCubit.stream) @@ -54,10 +55,10 @@ void main() { email: any(named: 'email'), password: any(named: 'password'), otp: any(named: 'otp'))) - .thenAnswer((_) async => DerivAuthLoggedInState( - authorizeEntity: const AuthorizeEntity(), - landingCompany: const LandingCompanyEntity(), - )); + .thenAnswer((_) async => DerivAuthLoggedInState(const DerivAuthModel( + authorizeEntity: AuthorizeEntity(), + landingCompany: LandingCompanyEntity(), + ))); await $.pumpApp( BlocProvider.value( diff --git a/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart b/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart index 101e4be1e..8d0655d0e 100644 --- a/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart @@ -9,7 +9,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -23,7 +23,7 @@ void main() { setUpAll(() => authCubit = MockAuthCubit()); - patrolTest( + patrolWidgetTest( 'renders email and password field including social auth buttons.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); @@ -54,7 +54,7 @@ void main() { expect($(DerivSocialAuthPanel), findsOneWidget); }); - patrolTest('displays invalid email error on invalid email typed.', + patrolWidgetTest('displays invalid email error on invalid email typed.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); const invalidEmail = 'invalid-email'; @@ -87,7 +87,7 @@ void main() { expect($(Text).$('Enter a valid email address'), findsOneWidget); }); - patrolTest('displays loading error on AuthLoadingState', + patrolWidgetTest('displays loading error on AuthLoadingState', (PatrolTester $) async { final mockAuthState = DerivAuthLoadingState(); @@ -114,7 +114,7 @@ void main() { expect($(LoadingIndicator), findsOneWidget); }); - patrolTest('calls signupTapped when signup button is pressed.', + patrolWidgetTest('calls signupTapped when signup button is pressed.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); @@ -149,11 +149,12 @@ void main() { expect(onSignupTappedCalled, isTrue); }); - patrolTest('calls onLoggedIn on successful login.', (PatrolTester $) async { - final mockAuthState = DerivAuthLoggedInState( - authorizeEntity: const AuthorizeEntity(), - landingCompany: const LandingCompanyEntity(), - ); + patrolWidgetTest('calls onLoggedIn on successful login.', + (PatrolTester $) async { + final mockAuthState = DerivAuthLoggedInState(const DerivAuthModel( + authorizeEntity: AuthorizeEntity(), + landingCompany: LandingCompanyEntity(), + )); when(() => authCubit.state).thenAnswer((_) => mockAuthState); @@ -180,9 +181,13 @@ void main() { expect(onLoggedInCalled, isTrue); }); - patrolTest('calls onLoginError on login error.', (PatrolTester $) async { + patrolWidgetTest('calls onLoginError on login error.', + (PatrolTester $) async { final mockAuthState = DerivAuthErrorState( - message: 'error', type: AuthErrorType.failedAuthorization); + isSocialLogin: false, + message: 'error', + type: AuthErrorType.failedAuthorization, + ); when(() => authCubit.state).thenAnswer((_) => mockAuthState); @@ -209,7 +214,7 @@ void main() { expect(onLoginErrorCalled, isTrue); }); - patrolTest('calls resetPassTapped when reset button is pressed.', + patrolWidgetTest('calls resetPassTapped when reset button is pressed.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); @@ -240,7 +245,7 @@ void main() { expect(onResetPassTappedCalled, isTrue); }); - patrolTest( + patrolWidgetTest( 'calls onSocialAuthButtonPressed when social auth button is pressed.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); diff --git a/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_choose_new_pass_layout_test.dart b/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_choose_new_pass_layout_test.dart index b351286c8..61470248b 100644 --- a/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_choose_new_pass_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_choose_new_pass_layout_test.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -21,7 +21,7 @@ void main() { mockResetPassCubit = MockDerivResetPassCubit(); }); - patrolTest('should render DerivChooseNewPassLayout', + patrolWidgetTest('should render DerivChooseNewPassLayout', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); @@ -43,7 +43,7 @@ void main() { expect($(DerivChooseNewPassLayout), findsOneWidget); }); - patrolTest( + patrolWidgetTest( 'should call onResetPassError when state is DerivResetPassErrorState', (PatrolTester $) async { const errorState = DerivResetPassErrorState(errorMessage: 'test error'); @@ -70,7 +70,7 @@ void main() { expect(onResetPassErrorCalled, isTrue); }); - patrolTest( + patrolWidgetTest( 'should call onResetPassSucceed after 2 seconds when state is DerivResetPassPasswordChangedState', (PatrolTester $) async { const passwordChangedState = DerivResetPassPasswordChangedState(); @@ -101,7 +101,7 @@ void main() { expect(onResetPassSucceedCalled, isTrue); }); - patrolTest('should call changePassword when input is valid', + patrolWidgetTest('should call changePassword when input is valid', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); const newPassTest = 'newPassWordTest123@'; @@ -131,7 +131,7 @@ void main() { token: token, newPassword: newPassTest)).called(1); }); - patrolTest('should not call changePassword when input is invalid', + patrolWidgetTest('should not call changePassword when input is invalid', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); const newPassTest = 'pass'; diff --git a/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_reset_pass_layout_test.dart b/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_reset_pass_layout_test.dart index 5f1df8405..6591fefef 100644 --- a/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_reset_pass_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_reset_pass_layout_test.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -20,7 +20,8 @@ void main() { mockResetPassCubit = MockDerivResetPassCubit(); }); - patrolTest('should render DerivResetPassLayout', (PatrolTester $) async { + patrolWidgetTest('should render DerivResetPassLayout', + (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); when(() => mockResetPassCubit.state).thenAnswer((_) => resetPassState); @@ -39,7 +40,7 @@ void main() { expect($(DerivResetPassLayout), findsOneWidget); }); - patrolTest( + patrolWidgetTest( 'should show emailSentPage when state is DerivResetPassEmailSentState', (PatrolTester $) async { const resetPassState = DerivResetPassEmailSentState(); @@ -63,7 +64,7 @@ void main() { findsOneWidget); }); - patrolTest( + patrolWidgetTest( 'should show submitEmailForm when state is DerivResetPassInitialState', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); @@ -87,7 +88,8 @@ void main() { findsOneWidget); }); - patrolTest('should not call sendVerificationEmail when email is invalid', + patrolWidgetTest( + 'should not call sendVerificationEmail when email is invalid', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); const email = 'wrongEmail@format'; @@ -115,7 +117,7 @@ void main() { verifyNever(() => mockResetPassCubit.sendVerificationEmail(email)); }); - patrolTest('should call sendVerificationEmail when email is valid', + patrolWidgetTest('should call sendVerificationEmail when email is valid', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); const email = 'correctEmail@format.com'; @@ -145,7 +147,7 @@ void main() { verify(() => mockResetPassCubit.sendVerificationEmail(email)).called(1); }); - patrolTest( + patrolWidgetTest( 'should call OnResetPassError when state is DerivResetPassErrorState', (PatrolTester $) async { const errorState = DerivResetPassErrorState(errorMessage: 'test error'); diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_country_selection_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_country_selection_layout_test.dart index 6efff970b..7ed4ddd4a 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_country_selection_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_country_selection_layout_test.dart @@ -2,7 +2,7 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_ui/deriv_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; @@ -13,13 +13,14 @@ void main() { setUp(() { residences = Future>.value([ - const DerivResidenceModel(code: 'ID', name: 'Indonesia', isDisabled: false), + const DerivResidenceModel( + code: 'ID', name: 'Indonesia', isDisabled: false), const DerivResidenceModel( code: 'UK', name: 'England', isDisabled: true), ]); }); - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp(DerivCountrySelectionLayout( onNextPressed: () {}, verificationCode: '123456', diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_email_not_received_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_email_not_received_layout_test.dart index 910b3b3fb..969365d75 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_email_not_received_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_email_not_received_layout_test.dart @@ -1,13 +1,13 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; void main() { group('DerivEmailNotReceivedLayout', () { - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp(DerivEmailNotReceivedLayout( onReEnterEmailPressed: () {}, )); @@ -17,7 +17,8 @@ void main() { findsOneWidget); }); - patrolTest('onReEnterEmailPressed is called when tapped on the button', + patrolWidgetTest( + 'onReEnterEmailPressed is called when tapped on the button', (PatrolTester $) async { bool isReEnterEmailPressed = false; diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart index 4e6ede2a4..af45d1750 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -31,7 +31,7 @@ void main() { }); group('DerivSetPasswordLayout', () { - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp( settle: false, MultiBlocProvider( @@ -53,7 +53,7 @@ void main() { expect($(ElevatedButton), findsNWidgets(2)); }); - patrolTest('onDerivAuthState is called on DerivAuth state changes', + patrolWidgetTest('onDerivAuthState is called on DerivAuth state changes', (PatrolTester $) async { bool isOnDerivAuthStateCalled = false; @@ -78,7 +78,8 @@ void main() { expect(isOnDerivAuthStateCalled, true); }); - patrolTest('onDerivSignupState is called on DerivSignup state changes', + patrolWidgetTest( + 'onDerivSignupState is called on DerivSignup state changes', (PatrolTester $) async { bool isOnDerivSignupStateCalled = false; @@ -106,7 +107,7 @@ void main() { expect(isOnDerivSignupStateCalled, true); }); - patrolTest('onPreviousPressed is called upon tapping previous button', + patrolWidgetTest('onPreviousPressed is called upon tapping previous button', (PatrolTester $) async { bool isOnPreviousPressedCalled = false; @@ -130,7 +131,8 @@ void main() { expect(isOnPreviousPressedCalled, true); }); - patrolTest('password is no longer obscure after visibility icon pressed', + patrolWidgetTest( + 'password is no longer obscure after visibility icon pressed', (PatrolTester $) async { await $.pumpApp( settle: false, @@ -158,7 +160,7 @@ void main() { findsOneWidget); }); - patrolTest('start trading button is disabled until password is valid', + patrolWidgetTest('start trading button is disabled until password is valid', (PatrolTester $) async { const validPassword = 'Abcdefg123'; diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart index c3186feb6..2da333238 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -30,7 +30,7 @@ void main() { (_) => Stream.fromIterable([const DerivSignupInitialState()])); }); - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp( settle: false, BlocProvider.value( @@ -52,7 +52,7 @@ void main() { expect($(DerivSocialAuthPanel), findsOneWidget); }); - patrolTest( + patrolWidgetTest( 'onSocialAuthButtonPressed is called upon tapping social auth option', (PatrolTester $) async { bool isOnSocialAuthButtonPressedCalled = false; @@ -77,7 +77,7 @@ void main() { expect(isOnSocialAuthButtonPressedCalled, true); }); - patrolTest('onSignupEmailSent is called upon sign up email sent', + patrolWidgetTest('onSignupEmailSent is called upon sign up email sent', (PatrolTester $) async { bool isOnSignupEmailSentCalled = false; @@ -103,7 +103,7 @@ void main() { expect(isOnSignupEmailSentCalled, true); }); - patrolTest('onSignupPressed is called upon tapping signup button', + patrolWidgetTest('onSignupPressed is called upon tapping signup button', (PatrolTester $) async { bool isOnSignupPressedCalled = false; @@ -134,7 +134,7 @@ void main() { .called(1); }); - patrolTest('onLoginTapped is called upon tapping login button', + patrolWidgetTest('onLoginTapped is called upon tapping login button', (PatrolTester $) async { bool isOnLoginTappedCalled = false; @@ -161,7 +161,7 @@ void main() { expect(isOnLoginTappedCalled, true); }); - patrolTest('onSignupError is called upon signup error state', + patrolWidgetTest('onSignupError is called upon signup error state', (PatrolTester $) async { bool isOnSignupErrorCalled = false; diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verification_done_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verification_done_layout_test.dart index 36bcb5202..361a14fe1 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verification_done_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verification_done_layout_test.dart @@ -1,13 +1,13 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; void main() { group('DerivVerificationDoneLayout', () { - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp(DerivVerificationDoneLayout( verificationCode: '123456', onContinuePressed: () {}, @@ -18,7 +18,7 @@ void main() { expect($(ElevatedButton).$('Continue'), findsOneWidget); }); - patrolTest('onContinuePressed is called', (PatrolTester $) async { + patrolWidgetTest('onContinuePressed is called', (PatrolTester $) async { bool isContinuePressed = false; await $.pumpApp(DerivVerificationDoneLayout( diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verify_email_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verify_email_layout_test.dart index da35b53cc..322b961bd 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verify_email_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verify_email_layout_test.dart @@ -1,7 +1,7 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; @@ -13,7 +13,7 @@ void main() { testEmail = 'test@gmail.com'; }); - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp(DerivVerifyEmailLayout( email: testEmail, onEmailNotReceivedPressed: () {}, @@ -25,7 +25,8 @@ void main() { expect($(Text).$('Check your email'), findsOneWidget); }); - patrolTest('onEmailNotReceivedPressed is called when tapped on the button', + patrolWidgetTest( + 'onEmailNotReceivedPressed is called when tapped on the button', (PatrolTester $) async { bool isEmailNotReceivedPressed = false; diff --git a/packages/deriv_auth_ui/test/features/signup/models/deriv_auth_utm_model_test.dart b/packages/deriv_auth_ui/test/features/signup/models/deriv_auth_utm_model_test.dart index daa546800..37d569f3c 100644 --- a/packages/deriv_auth_ui/test/features/signup/models/deriv_auth_utm_model_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/models/deriv_auth_utm_model_test.dart @@ -2,11 +2,11 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; void main() { group('DerivAuthUtmModel', () { - patrolTest('Constructor initializes all properties correctly', + patrolWidgetTest('Constructor initializes all properties correctly', (PatrolTester $) async { final model = DerivAuthUtmModel( affiliateToken: 'affiliateToken', @@ -31,9 +31,10 @@ void main() { expect(model.affiliateToken, 'affiliateToken'); }); - patrolTest('Constructor initializes optional properties as null', + patrolWidgetTest('Constructor initializes optional properties as null', (PatrolTester $) async { - final model = DerivAuthUtmModel(utmSource: 'source', utmCampaign: 'campaign'); + final model = + DerivAuthUtmModel(utmSource: 'source', utmCampaign: 'campaign'); expect(model.utmCampaignId, isNull); expect(model.utmAdGroupId, isNull); diff --git a/packages/deriv_auth_ui/test/features/signup/models/deriv_password_policy_model_test.dart b/packages/deriv_auth_ui/test/features/signup/models/deriv_password_policy_model_test.dart index 6a23e8b08..02515b627 100644 --- a/packages/deriv_auth_ui/test/features/signup/models/deriv_password_policy_model_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/models/deriv_password_policy_model_test.dart @@ -1,13 +1,13 @@ - // ignore_for_file: always_specify_types import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; void main() { group('DerivPasswordPolicyModel', () { - patrolTest('isMatchWith() returns true for matching password', (PatrolTester $) async { + patrolWidgetTest('isMatchWith() returns true for matching password', + (PatrolTester $) async { final policy = DerivPasswordPolicyModel( description: 'At least 8 characters', regex: RegExp(r'.{8,}'), @@ -16,7 +16,8 @@ void main() { expect(policy.isMatchWith('password123'), isTrue); }); - patrolTest('isMatchWith() returns false for non-matching password', (PatrolTester $) async { + patrolWidgetTest('isMatchWith() returns false for non-matching password', + (PatrolTester $) async { final policy = DerivPasswordPolicyModel( description: 'At least 8 characters', regex: RegExp(r'.{8,}'), @@ -25,7 +26,9 @@ void main() { expect(policy.isMatchWith('pass'), isFalse); }); - patrolTest('isMatchWith() returns true for optional policy with empty password', (PatrolTester $) async { + patrolWidgetTest( + 'isMatchWith() returns true for optional policy with empty password', + (PatrolTester $) async { final policy = DerivPasswordPolicyModel( description: 'Optional policy', regex: RegExp(r'^$'), diff --git a/packages/deriv_auth_ui/test/features/signup/models/deriv_residence_model_test.dart b/packages/deriv_auth_ui/test/features/signup/models/deriv_residence_model_test.dart index 9f1ca143a..e68a54495 100644 --- a/packages/deriv_auth_ui/test/features/signup/models/deriv_residence_model_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/models/deriv_residence_model_test.dart @@ -2,11 +2,11 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; void main() { group('DerivResidenceModel', () { - patrolTest('Constructor initializes all properties correctly', + patrolWidgetTest('Constructor initializes all properties correctly', (PatrolTester $) async { const model = DerivResidenceModel( isDisabled: true, diff --git a/packages/deriv_auth_ui/test/features/signup/widgets/country_selection_list_widget_test.dart b/packages/deriv_auth_ui/test/features/signup/widgets/country_selection_list_widget_test.dart index 54ac56f6f..0d9cf7812 100644 --- a/packages/deriv_auth_ui/test/features/signup/widgets/country_selection_list_widget_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/widgets/country_selection_list_widget_test.dart @@ -2,7 +2,7 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_auth_ui/src/features/signup/widgets/country_selection_list_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; @@ -12,13 +12,14 @@ void main() { setUpAll(() { residences = [ - const DerivResidenceModel(code: 'ID', name: 'Indonesia', isDisabled: false), + const DerivResidenceModel( + code: 'ID', name: 'Indonesia', isDisabled: false), const DerivResidenceModel( code: 'UK', name: 'England', isDisabled: true), ]; }); - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp( CountrySelectionListWidget(countries: residences, onChanged: (_) {})); @@ -28,7 +29,7 @@ void main() { expect($(Text).$('England'), findsOneWidget); }); - patrolTest('onChanged is called', (PatrolTester $) async { + patrolWidgetTest('onChanged is called', (PatrolTester $) async { bool isOnChangedCalled = false; await $.pumpApp(CountrySelectionListWidget( @@ -40,7 +41,7 @@ void main() { expect(isOnChangedCalled, true); }); - patrolTest('search field appears on tapping search', + patrolWidgetTest('search field appears on tapping search', (PatrolTester $) async { await $.pumpApp(CountrySelectionListWidget( countries: residences, onChanged: (int country) => null)); diff --git a/packages/deriv_auth_ui/test/pump_app.dart b/packages/deriv_auth_ui/test/pump_app.dart index 3f74dd32a..a290d6116 100644 --- a/packages/deriv_auth_ui/test/pump_app.dart +++ b/packages/deriv_auth_ui/test/pump_app.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import 'base_test_app.dart'; From 4577f692ec221ce96d7a2ad917f04b498cc7967c Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Thu, 26 Oct 2023 10:59:16 +0800 Subject: [PATCH 02/44] deps: update deriv_theme in deriv_ui --- packages/deriv_ui/pubspec.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/deriv_ui/pubspec.yaml b/packages/deriv_ui/pubspec.yaml index 36734d6d8..a689b2616 100644 --- a/packages/deriv_ui/pubspec.yaml +++ b/packages/deriv_ui/pubspec.yaml @@ -12,12 +12,10 @@ dependencies: sdk: flutter deriv_theme: - path: - ../deriv_theme - # git: - # url: git@github.com:regentmarkets/flutter-deriv-packages.git - # path: packages/deriv_theme - # ref: dev + git: + url: git@github.com:regentmarkets/flutter-deriv-packages.git + path: packages/deriv_theme + ref: dev deriv_web_view: git: From a991d47c99f315c02d871d5605ce7d9d873ab234 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Thu, 26 Oct 2023 14:13:23 +0800 Subject: [PATCH 03/44] wip: auth-ui-update --- .../helpers/auth_error_state_handler.dart | 41 +++++++++++++++++++ .../login/layouts/deriv_login_layout.dart | 32 +++++++++++++-- 2 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 packages/deriv_auth_ui/lib/src/core/helpers/auth_error_state_handler.dart diff --git a/packages/deriv_auth_ui/lib/src/core/helpers/auth_error_state_handler.dart b/packages/deriv_auth_ui/lib/src/core/helpers/auth_error_state_handler.dart new file mode 100644 index 000000000..656bf7ddb --- /dev/null +++ b/packages/deriv_auth_ui/lib/src/core/helpers/auth_error_state_handler.dart @@ -0,0 +1,41 @@ +import 'package:deriv_auth/deriv_auth.dart'; + +/// {@template auth_state_listener} +/// An abstract class that represents all the possible case of the +/// [DerivAuthErrorState]. Each method will be called when the corresponding +/// error state is emitted and can be implemented by the client app to handle +/// the error. +/// {@endtemplate} +abstract class AuthErrorStateHandler { + /// {@macro auth_state_listener} + + /// User has set up 2FA and needs to enter 2FA. + void onMissingOtp(); + + /// Account is self closed. + void onSelfClosed(); + + /// Account is not supported in the country. + void onUnsupportedCountry(); + + /// Invalid credentials. + void onInvalidCredentials(); + + /// Account is unavailable. + void onAccountUnavailable(); + + /// Invalid 2FA code. + void invalid2faCode(); + + /// Failed authorization. + void onFailedAuthorization(); + + /// User is trying to authenticate from an unsupported residence. + void onInavlidResidence(); + + /// User is trying to authenticate from an expired account. + void onExpiredAccount(); + + /// Default error handler for unexpected case. + void defaultError(); +} diff --git a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart index 4f89a1763..dac69c505 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; +import 'package:deriv_auth_ui/src/core/helpers/auth_error_state_handler.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_divider.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_panel.dart'; import 'package:deriv_theme/deriv_theme.dart'; @@ -31,8 +32,8 @@ class DerivLoginLayout extends StatefulWidget { /// Callback to be called when signup button is tapped. final VoidCallback onSignupTapped; - /// Callback to be called when login button is tapped. - final Function(DerivAuthErrorState) onLoginError; + /// Implementation of [AuthErrorStateHandler]. + final AuthErrorStateHandler onLoginError; /// Callback to be called when user is logged in. final Function(DerivAuthLoggedInState) onLoggedIn; @@ -262,7 +263,32 @@ class _DerivLoginLayoutState extends State { void _onAuthState(BuildContext context, DerivAuthState state) { if (state is DerivAuthErrorState) { - widget.onLoginError.call(state); + switch (state.type) { + case AuthErrorType.missingOtp: + widget.onLoginError.onMissingOtp(); + return; + case AuthErrorType.selfClosed: + widget.onLoginError.onSelfClosed(); + return; + case AuthErrorType.unsupportedCountry: + widget.onLoginError.onUnsupportedCountry(); + return; + case AuthErrorType.accountUnavailable: + widget.onLoginError.onAccountUnavailable(); + return; + case AuthErrorType.invalidCredential: + widget.onLoginError.onInvalidCredentials(); + return; + case AuthErrorType.invalid2faCode: + widget.onLoginError.invalid2faCode(); + return; + case AuthErrorType.failedAuthorization: + widget.onLoginError.onFailedAuthorization(); + return; + default: + widget.onLoginError.defaultError(); + return; + } } if (state is DerivAuthLoggedInState) { From 1720d56474c44320d1cbcb0b47273fbaa5a809e3 Mon Sep 17 00:00:00 2001 From: Istiak Date: Mon, 9 Oct 2023 14:29:35 +0800 Subject: [PATCH 04/44] added invalidresidence errror mapping for social login --- packages/deriv_auth/lib/core/constants/constants.dart | 3 +++ .../deriv_auth/lib/core/models/auth_error/auth_error.dart | 3 +++ .../lib/features/auth/services/deriv_auth_service.dart | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/packages/deriv_auth/lib/core/constants/constants.dart b/packages/deriv_auth/lib/core/constants/constants.dart index 61f437786..0b81b3504 100644 --- a/packages/deriv_auth/lib/core/constants/constants.dart +++ b/packages/deriv_auth/lib/core/constants/constants.dart @@ -16,6 +16,9 @@ const String selfClosedError = 'SELF_CLOSED'; /// Error occurs if tries to login on deactivated account. const String accountUnavailableError = 'AccountUnavailable'; +/// Error occurs if tries to login from unsupported country. +const String invalidResidence = 'INVALID_RESIDENCE'; + /// Error message when user's country is not accepted. const String notAvailableCountryMessage = 'This service is not available in your country.'; diff --git a/packages/deriv_auth/lib/core/models/auth_error/auth_error.dart b/packages/deriv_auth/lib/core/models/auth_error/auth_error.dart index ba150f583..f5ce2a2ff 100644 --- a/packages/deriv_auth/lib/core/models/auth_error/auth_error.dart +++ b/packages/deriv_auth/lib/core/models/auth_error/auth_error.dart @@ -35,4 +35,7 @@ enum AuthErrorType { /// Error occurs if tries to login on deactivated account. accountUnavailable, + + /// Error occurs if tries to login from unsupported country. + invalidResidence, } diff --git a/packages/deriv_auth/lib/features/auth/services/deriv_auth_service.dart b/packages/deriv_auth/lib/features/auth/services/deriv_auth_service.dart index 505eba511..3aabac973 100644 --- a/packages/deriv_auth/lib/features/auth/services/deriv_auth_service.dart +++ b/packages/deriv_auth/lib/features/auth/services/deriv_auth_service.dart @@ -196,6 +196,12 @@ class DerivAuthService extends BaseAuthService { message: exception.message, ); + case invalidResidence: + return DerivAuthException( + type: AuthErrorType.invalidResidence, + message: exception.message, + ); + default: return DerivAuthException( type: AuthErrorType.failedAuthorization, From 6818ff321b0875a039013e339e55cabba683a4fb Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Thu, 26 Oct 2023 18:36:13 +0800 Subject: [PATCH 05/44] feat: add deriv auth error state handler --- .../lib/features/login/pages/login_page.dart | 1 + packages/deriv_auth_ui/example/pubspec.yaml | 9 +- packages/deriv_auth_ui/lib/deriv_auth_ui.dart | 1 + .../lib/generated/intl/messages_en.dart | 23 ++++ .../deriv_auth_ui/lib/generated/l10n.dart | 120 ++++++++++++++++++ packages/deriv_auth_ui/lib/l10n/intl_en.arb | 14 +- .../auth_error_state_handler.dart | 22 ++-- .../core/states/auth_error_state_mapper.dart | 45 +++++++ .../default_auth_error_state_handler.dart | 108 ++++++++++++++++ .../lib/src/core/states/states.dart | 3 + .../login/layouts/deriv_login_layout.dart | 43 ++----- packages/deriv_auth_ui/pubspec.yaml | 5 +- 12 files changed, 345 insertions(+), 49 deletions(-) rename packages/deriv_auth_ui/lib/src/core/{helpers => states}/auth_error_state_handler.dart (57%) create mode 100644 packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart create mode 100644 packages/deriv_auth_ui/lib/src/core/states/default_auth_error_state_handler.dart create mode 100644 packages/deriv_auth_ui/lib/src/core/states/states.dart diff --git a/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart b/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart index 7d1e66a2a..e1f8e6168 100644 --- a/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart +++ b/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart @@ -32,6 +32,7 @@ class _LoginPageState extends State { builder: (context) => const HomePage(), ), ), + authErrorStateHandler: DefaultAuthErrorStateHandler(context: context), onLoginError: (_) {}, onResetPassTapped: () => Navigator.push( context, diff --git a/packages/deriv_auth_ui/example/pubspec.yaml b/packages/deriv_auth_ui/example/pubspec.yaml index f21400657..e8fb411c6 100644 --- a/packages/deriv_auth_ui/example/pubspec.yaml +++ b/packages/deriv_auth_ui/example/pubspec.yaml @@ -15,10 +15,11 @@ dependencies: deriv_auth_ui: path: ../ deriv_auth: - git: - url: git@github.com:regentmarkets/flutter-deriv-packages.git - path: packages/deriv_auth - ref: dev + # git: + # url: git@github.com:regentmarkets/flutter-deriv-packages.git + # path: packages/deriv_auth + # ref: dev + path: ../../deriv_auth deriv_theme: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git diff --git a/packages/deriv_auth_ui/lib/deriv_auth_ui.dart b/packages/deriv_auth_ui/lib/deriv_auth_ui.dart index c03e33998..866264414 100644 --- a/packages/deriv_auth_ui/lib/deriv_auth_ui.dart +++ b/packages/deriv_auth_ui/lib/deriv_auth_ui.dart @@ -15,3 +15,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'; diff --git a/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart b/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart index 927d0cfc3..2a10c67ce 100644 --- a/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart +++ b/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart @@ -51,20 +51,37 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Reset my password"), "actionStartTrading": MessageLookupByLibrary.simpleMessage("Start trading"), + "actionTryAgain": MessageLookupByLibrary.simpleMessage("Try Again"), "infoReferralInfoDescription": MessageLookupByLibrary.simpleMessage( "An alphanumeric code provided by a Deriv affiliate, applicable for email sign-ups only."), + "informDeactivatedAccount": MessageLookupByLibrary.simpleMessage( + "Your account is deactivated."), "informEnterTwoFactorAuthCode": MessageLookupByLibrary.simpleMessage( "Enter the 6-digit code from the authenticator app on your phone."), + "informExpiredAccount": + MessageLookupByLibrary.simpleMessage("Your account is expired"), + "informFailedAuthentication": MessageLookupByLibrary.simpleMessage( + "Your email or password may be incorrect. Did you sign up with a social account? Check and try again."), + "informFailedAuthorization": + MessageLookupByLibrary.simpleMessage("Authorization failed."), + "informInvalid2FACode": MessageLookupByLibrary.simpleMessage( + "The code you entered is invalid. Check and try again."), + "informInvalidCredentials": + MessageLookupByLibrary.simpleMessage("Invalid credentials."), "informInvalidEmailFormat": MessageLookupByLibrary.simpleMessage("Enter a valid email address"), "informInvalidPasswordFormat": MessageLookupByLibrary.simpleMessage( "Please enter a valid password format"), "informInvalidReferralCode": MessageLookupByLibrary.simpleMessage( "The referral code you entered is invalid. Check and try again."), + "informInvalidResidence": + MessageLookupByLibrary.simpleMessage("Invalid residence."), "informLetsContinue": MessageLookupByLibrary.simpleMessage("Let\'s continue."), "informLoginOptions": MessageLookupByLibrary.simpleMessage("Or log in with"), + "informMissingOtp": + MessageLookupByLibrary.simpleMessage("Missing one-time password."), "informPasswordPolicy": MessageLookupByLibrary.simpleMessage("Your password must have:"), "informPasswordPolicyLength": @@ -78,7 +95,13 @@ class MessageLookup extends MessageLookupByLibrary { "You’ll need to log in with your new password. Hang on, we’re redirecting you."), "informResetPassByEmail": MessageLookupByLibrary.simpleMessage( "We\'ll email you instructions to reset your password."), + "informSelfClosed": MessageLookupByLibrary.simpleMessage( + "Your account has been closed."), "informSendResetPasswordEmail": m0, + "informUnexpectedError": MessageLookupByLibrary.simpleMessage( + "An unexpected error occurred."), + "informUnsupportedCountry": MessageLookupByLibrary.simpleMessage( + "Your country is not supported."), "informVerificationEmailSent": m1, "informYourPassHasBeenReset": MessageLookupByLibrary.simpleMessage( "Your password has been reset"), diff --git a/packages/deriv_auth_ui/lib/generated/l10n.dart b/packages/deriv_auth_ui/lib/generated/l10n.dart index 7d4ef2308..0028dc351 100644 --- a/packages/deriv_auth_ui/lib/generated/l10n.dart +++ b/packages/deriv_auth_ui/lib/generated/l10n.dart @@ -690,6 +690,126 @@ class DerivAuthUILocalization { args: [], ); } + + /// `Try Again` + String get actionTryAgain { + return Intl.message( + 'Try Again', + name: 'actionTryAgain', + desc: '', + args: [], + ); + } + + /// `The code you entered is invalid. Check and try again.` + String get informInvalid2FACode { + return Intl.message( + 'The code you entered is invalid. Check and try again.', + name: 'informInvalid2FACode', + desc: '', + args: [], + ); + } + + /// `Your email or password may be incorrect. Did you sign up with a social account? Check and try again.` + String get informFailedAuthentication { + return Intl.message( + 'Your email or password may be incorrect. Did you sign up with a social account? Check and try again.', + name: 'informFailedAuthentication', + desc: '', + args: [], + ); + } + + /// `Your account is deactivated.` + String get informDeactivatedAccount { + return Intl.message( + 'Your account is deactivated.', + name: 'informDeactivatedAccount', + desc: '', + args: [], + ); + } + + /// `Authorization failed.` + String get informFailedAuthorization { + return Intl.message( + 'Authorization failed.', + name: 'informFailedAuthorization', + desc: '', + args: [], + ); + } + + /// `Invalid residence.` + String get informInvalidResidence { + return Intl.message( + 'Invalid residence.', + name: 'informInvalidResidence', + desc: '', + args: [], + ); + } + + /// `Invalid credentials.` + String get informInvalidCredentials { + return Intl.message( + 'Invalid credentials.', + name: 'informInvalidCredentials', + desc: '', + args: [], + ); + } + + /// `Missing one-time password.` + String get informMissingOtp { + return Intl.message( + 'Missing one-time password.', + name: 'informMissingOtp', + desc: '', + args: [], + ); + } + + /// `Your account has been closed.` + String get informSelfClosed { + return Intl.message( + 'Your account has been closed.', + name: 'informSelfClosed', + desc: '', + args: [], + ); + } + + /// `An unexpected error occurred.` + String get informUnexpectedError { + return Intl.message( + 'An unexpected error occurred.', + name: 'informUnexpectedError', + desc: '', + args: [], + ); + } + + /// `Your country is not supported.` + String get informUnsupportedCountry { + return Intl.message( + 'Your country is not supported.', + name: 'informUnsupportedCountry', + desc: '', + args: [], + ); + } + + /// `Your account is expired` + String get informExpiredAccount { + return Intl.message( + 'Your account is expired', + name: 'informExpiredAccount', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate diff --git a/packages/deriv_auth_ui/lib/l10n/intl_en.arb b/packages/deriv_auth_ui/lib/l10n/intl_en.arb index 337c4193e..16389d679 100644 --- a/packages/deriv_auth_ui/lib/l10n/intl_en.arb +++ b/packages/deriv_auth_ui/lib/l10n/intl_en.arb @@ -89,5 +89,17 @@ "informPasswordPolicyLowerAndUpper": "Upper and lower case letters", "informPasswordPolicyNumber": "At least one number", "warnPasswordContainsSymbol": "Use symbols for strong password.", - "labelReferralCode": "Referral Code" + "labelReferralCode": "Referral Code", + "actionTryAgain": "Try Again", + "informInvalid2FACode": "The code you entered is invalid. Check and try again.", + "informFailedAuthentication": "Your email or password may be incorrect. Did you sign up with a social account? Check and try again.", + "informDeactivatedAccount": "Your account is deactivated.", + "informFailedAuthorization": "Authorization failed.", + "informInvalidResidence": "Invalid residence.", + "informInvalidCredentials": "Invalid credentials.", + "informMissingOtp": "Missing one-time password.", + "informSelfClosed": "Your account has been closed.", + "informUnexpectedError": "An unexpected error occurred.", + "informUnsupportedCountry": "Your country is not supported.", + "informExpiredAccount": "Your account is expired" } \ No newline at end of file diff --git a/packages/deriv_auth_ui/lib/src/core/helpers/auth_error_state_handler.dart b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart similarity index 57% rename from packages/deriv_auth_ui/lib/src/core/helpers/auth_error_state_handler.dart rename to packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart index 656bf7ddb..f589bb2f5 100644 --- a/packages/deriv_auth_ui/lib/src/core/helpers/auth_error_state_handler.dart +++ b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart @@ -4,38 +4,38 @@ import 'package:deriv_auth/deriv_auth.dart'; /// An abstract class that represents all the possible case of the /// [DerivAuthErrorState]. Each method will be called when the corresponding /// error state is emitted and can be implemented by the client app to handle -/// the error. +/// the error. This class is also to make sure that each error case is handled. /// {@endtemplate} abstract class AuthErrorStateHandler { /// {@macro auth_state_listener} /// User has set up 2FA and needs to enter 2FA. - void onMissingOtp(); + void onMissingOtp(DerivAuthErrorState state); /// Account is self closed. - void onSelfClosed(); + void onSelfClosed(DerivAuthErrorState state); /// Account is not supported in the country. - void onUnsupportedCountry(); + void onUnsupportedCountry(DerivAuthErrorState state); /// Invalid credentials. - void onInvalidCredentials(); + void onInvalidCredentials(DerivAuthErrorState state); /// Account is unavailable. - void onAccountUnavailable(); + void onAccountUnavailable(DerivAuthErrorState state); /// Invalid 2FA code. - void invalid2faCode(); + void invalid2faCode(DerivAuthErrorState state); /// Failed authorization. - void onFailedAuthorization(); + void onFailedAuthorization(DerivAuthErrorState state); /// User is trying to authenticate from an unsupported residence. - void onInavlidResidence(); + void onInavlidResidence(DerivAuthErrorState state); /// User is trying to authenticate from an expired account. - void onExpiredAccount(); + void onExpiredAccount(DerivAuthErrorState state); /// Default error handler for unexpected case. - void defaultError(); + void onUnexpectedError(DerivAuthErrorState state); } diff --git a/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart new file mode 100644 index 000000000..4b8eb1a73 --- /dev/null +++ b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart @@ -0,0 +1,45 @@ +import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth_ui/src/core/states/auth_error_state_handler.dart'; + +/// Maps the [DerivAuthErrorState] to the corresponding [AuthErrorStateHandler]. +void authErrorStateMapper({ + required DerivAuthErrorState authErrorState, + required AuthErrorStateHandler authErrorStateHandler, + List? ignoredExceptions, +}) { + if (ignoredExceptions?.contains(authErrorState.type) ?? false) { + return; + } + switch (authErrorState.type) { + case AuthErrorType.missingOtp: + authErrorStateHandler.onMissingOtp(authErrorState); + return; + case AuthErrorType.selfClosed: + authErrorStateHandler.onSelfClosed(authErrorState); + return; + case AuthErrorType.unsupportedCountry: + authErrorStateHandler.onUnsupportedCountry(authErrorState); + return; + case AuthErrorType.accountUnavailable: + authErrorStateHandler.onAccountUnavailable(authErrorState); + return; + case AuthErrorType.invalidCredential: + authErrorStateHandler.onInvalidCredentials(authErrorState); + return; + case AuthErrorType.invalid2faCode: + authErrorStateHandler.invalid2faCode(authErrorState); + return; + case AuthErrorType.failedAuthorization: + authErrorStateHandler.onFailedAuthorization(authErrorState); + return; + case AuthErrorType.invalidResidence: + authErrorStateHandler.onInavlidResidence(authErrorState); + return; + case AuthErrorType.expiredAccount: + authErrorStateHandler.onExpiredAccount(authErrorState); + return; + default: + authErrorStateHandler.onUnexpectedError(authErrorState); + return; + } +} diff --git a/packages/deriv_auth_ui/lib/src/core/states/default_auth_error_state_handler.dart b/packages/deriv_auth_ui/lib/src/core/states/default_auth_error_state_handler.dart new file mode 100644 index 000000000..156a5877d --- /dev/null +++ b/packages/deriv_auth_ui/lib/src/core/states/default_auth_error_state_handler.dart @@ -0,0 +1,108 @@ +import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; +import 'package:deriv_auth_ui/src/core/states/auth_error_state_handler.dart'; +import 'package:deriv_ui/deriv_ui.dart'; +import 'package:flutter/material.dart'; + +/// {@template default_auth_error_state_handler} +/// Default implementation of [AuthErrorStateHandler]. +/// {@endtemplate} +class DefaultAuthErrorStateHandler implements AuthErrorStateHandler { + /// {@macro default_auth_error_state_handler} + DefaultAuthErrorStateHandler({ + required this.context, + }); + + /// The [BuildContext] of the widget that is using this handler. + final BuildContext context; + + @override + void invalid2faCode(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informInvalid2FACode, + actionLabel: context.localization.actionTryAgain, + ); + } + + @override + void onAccountUnavailable(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informDeactivatedAccount, + actionLabel: context.localization.actionTryAgain, + ); + } + + @override + void onExpiredAccount(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informExpiredAccount, + actionLabel: context.localization.actionTryAgain, + ); + } + + @override + void onFailedAuthorization(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informFailedAuthorization, + actionLabel: context.localization.actionTryAgain, + ); + } + + @override + void onInavlidResidence(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informInvalidResidence, + actionLabel: context.localization.actionTryAgain, + ); + } + + @override + void onInvalidCredentials(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informInvalidCredentials, + actionLabel: context.localization.actionTryAgain, + ); + } + + @override + void onMissingOtp(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informMissingOtp, + actionLabel: context.localization.actionTryAgain, + ); + } + + @override + void onSelfClosed(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informSelfClosed, + actionLabel: context.localization.actionTryAgain, + ); + } + + @override + void onUnexpectedError(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informUnexpectedError, + actionLabel: context.localization.actionTryAgain, + ); + } + + @override + void onUnsupportedCountry(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informUnsupportedCountry, + actionLabel: context.localization.actionTryAgain, + ); + } +} diff --git a/packages/deriv_auth_ui/lib/src/core/states/states.dart b/packages/deriv_auth_ui/lib/src/core/states/states.dart new file mode 100644 index 000000000..bab4ee7d2 --- /dev/null +++ b/packages/deriv_auth_ui/lib/src/core/states/states.dart @@ -0,0 +1,3 @@ +export 'auth_error_state_handler.dart'; +export 'auth_error_state_mapper.dart'; +export 'default_auth_error_state_handler.dart'; diff --git a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart index dac69c505..976a754ec 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart @@ -3,7 +3,8 @@ import 'dart:async'; import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; -import 'package:deriv_auth_ui/src/core/helpers/auth_error_state_handler.dart'; +import 'package:deriv_auth_ui/src/core/states/auth_error_state_handler.dart'; +import 'package:deriv_auth_ui/src/core/states/auth_error_state_mapper.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_divider.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_panel.dart'; import 'package:deriv_theme/deriv_theme.dart'; @@ -17,11 +18,12 @@ class DerivLoginLayout extends StatefulWidget { const DerivLoginLayout({ required this.onResetPassTapped, required this.onSignupTapped, - required this.onLoginError, required this.onLoggedIn, required this.onSocialAuthButtonPressed, required this.welcomeLabel, required this.greetingLabel, + required this.authErrorStateHandler, + this.onLoginError, this.onLoginTapped, Key? key, }) : super(key: key); @@ -33,7 +35,10 @@ class DerivLoginLayout extends StatefulWidget { final VoidCallback onSignupTapped; /// Implementation of [AuthErrorStateHandler]. - final AuthErrorStateHandler onLoginError; + final AuthErrorStateHandler authErrorStateHandler; + + /// Callback to be called when login error occurs. + final Function(DerivAuthErrorState)? onLoginError; /// Callback to be called when user is logged in. final Function(DerivAuthLoggedInState) onLoggedIn; @@ -263,32 +268,12 @@ class _DerivLoginLayoutState extends State { void _onAuthState(BuildContext context, DerivAuthState state) { if (state is DerivAuthErrorState) { - switch (state.type) { - case AuthErrorType.missingOtp: - widget.onLoginError.onMissingOtp(); - return; - case AuthErrorType.selfClosed: - widget.onLoginError.onSelfClosed(); - return; - case AuthErrorType.unsupportedCountry: - widget.onLoginError.onUnsupportedCountry(); - return; - case AuthErrorType.accountUnavailable: - widget.onLoginError.onAccountUnavailable(); - return; - case AuthErrorType.invalidCredential: - widget.onLoginError.onInvalidCredentials(); - return; - case AuthErrorType.invalid2faCode: - widget.onLoginError.invalid2faCode(); - return; - case AuthErrorType.failedAuthorization: - widget.onLoginError.onFailedAuthorization(); - return; - default: - widget.onLoginError.defaultError(); - return; - } + widget.onLoginError?.call(state); + + authErrorStateMapper( + authErrorState: state, + authErrorStateHandler: widget.authErrorStateHandler, + ); } if (state is DerivAuthLoggedInState) { diff --git a/packages/deriv_auth_ui/pubspec.yaml b/packages/deriv_auth_ui/pubspec.yaml index ee846f0cf..96a7da6b3 100644 --- a/packages/deriv_auth_ui/pubspec.yaml +++ b/packages/deriv_auth_ui/pubspec.yaml @@ -15,10 +15,7 @@ dependencies: flutter_bloc: ^8.1.3 deriv_auth: - git: - url: git@github.com:regentmarkets/flutter-deriv-packages.git - path: packages/deriv_auth - ref: dev + path: ../deriv_auth deriv_theme: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git From b79101574ac82790c8acb3fb5cda93dfb6274500 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Thu, 26 Oct 2023 18:56:26 +0800 Subject: [PATCH 06/44] feat: add auth error state handler in sign up page --- .../features/signup/pages/signup_page.dart | 1 + .../signup/layouts/deriv_signup_layout.dart | 83 +++++---- .../layouts/deriv_login_layout_test.dart | 24 ++- .../layouts/deriv_signup_layout_test.dart | 159 +++++++++++------- packages/deriv_auth_ui/test/mocks.dart | 3 + 5 files changed, 166 insertions(+), 104 deletions(-) diff --git a/packages/deriv_auth_ui/example/lib/features/signup/pages/signup_page.dart b/packages/deriv_auth_ui/example/lib/features/signup/pages/signup_page.dart index c4667ba7f..f93304bfc 100644 --- a/packages/deriv_auth_ui/example/lib/features/signup/pages/signup_page.dart +++ b/packages/deriv_auth_ui/example/lib/features/signup/pages/signup_page.dart @@ -13,6 +13,7 @@ class SignupPage extends StatefulWidget { class _SignupPageState extends State { @override Widget build(BuildContext context) => DerivSignupLayout( + authErrorStateHandler: DefaultAuthErrorStateHandler(context: context), signupPageLabel: 'Start trading with Deriv', signupPageDescription: 'Join over 1 million traders worldwide who loves trading at Deriv.', diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart index 925e3a2a4..dbdc359d5 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart @@ -1,4 +1,5 @@ import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_divider.dart'; @@ -21,6 +22,7 @@ class DerivSignupLayout extends StatefulWidget { required this.onLoginTapped, required this.signupPageLabel, required this.signupPageDescription, + required this.authErrorStateHandler, this.enableReferralSection = true, Key? key, }) : super(key: key); @@ -31,6 +33,9 @@ class DerivSignupLayout extends StatefulWidget { /// Callback to be called when signup error occurs. final Function(DerivSignupErrorState) onSingupError; + /// Implementation of [AuthErrorStateHandler]. + final AuthErrorStateHandler authErrorStateHandler; + /// Callback to be called when signup email is sent. final Function(String) onSingupEmailSent; @@ -77,39 +82,42 @@ class _DerivSignupLayoutState extends State { Text(context.localization.labelSignUp, style: TextStyles.title), backgroundColor: context.theme.colors.secondary, ), - body: BlocConsumer( - listener: _onSignUpState, - builder: (BuildContext context, DerivSignupState state) => Form( - key: formKey, - child: SingleChildScrollView( - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: ThemeProvider.margin16, - vertical: ThemeProvider.margin24, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ..._buildHeaderSection(), - const SizedBox(height: ThemeProvider.margin24), - _buildEmailTextField(), - const SizedBox(height: ThemeProvider.margin36), - if (widget.enableReferralSection) _buildReferralSection(), - const SizedBox(height: ThemeProvider.margin16), - _buildSignUpButton(), - const SizedBox(height: ThemeProvider.margin24), - DerivSocialAuthDivider( - label: context.localization.labelOrSignUpWith, - ), - const SizedBox(height: ThemeProvider.margin24), - DerivSocialAuthPanel( - isEnabled: !isReferralEnabled, - onSocialAuthButtonPressed: - widget.onSocialAuthButtonPressed, - ), - const SizedBox(height: ThemeProvider.margin24), - _buildFooterSection(), - ], + body: BlocListener( + listener: _onAuthState, + child: BlocConsumer( + listener: _onSignUpState, + builder: (BuildContext context, DerivSignupState state) => Form( + key: formKey, + child: SingleChildScrollView( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: ThemeProvider.margin16, + vertical: ThemeProvider.margin24, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ..._buildHeaderSection(), + const SizedBox(height: ThemeProvider.margin24), + _buildEmailTextField(), + const SizedBox(height: ThemeProvider.margin36), + if (widget.enableReferralSection) _buildReferralSection(), + const SizedBox(height: ThemeProvider.margin16), + _buildSignUpButton(), + const SizedBox(height: ThemeProvider.margin24), + DerivSocialAuthDivider( + label: context.localization.labelOrSignUpWith, + ), + const SizedBox(height: ThemeProvider.margin24), + DerivSocialAuthPanel( + isEnabled: !isReferralEnabled, + onSocialAuthButtonPressed: + widget.onSocialAuthButtonPressed, + ), + const SizedBox(height: ThemeProvider.margin24), + _buildFooterSection(), + ], + ), ), ), ), @@ -299,6 +307,15 @@ class _DerivSignupLayoutState extends State { } } + void _onAuthState(BuildContext _, DerivAuthState state) { + if (state is DerivAuthErrorState) { + authErrorStateMapper( + authErrorState: state, + authErrorStateHandler: widget.authErrorStateHandler, + ); + } + } + bool _isFormValid() => _emailValidator(emailController.text) == null && _referralValidator(referralController.text) == null; diff --git a/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart b/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart index 8d0655d0e..1a97fe8d0 100644 --- a/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart @@ -14,6 +14,8 @@ import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; +class MockAuthStateErrorHandler extends Mock implements AuthErrorStateHandler {} + void main() { group('DerivLoginLayout', () { late MockAuthCubit authCubit; @@ -41,9 +43,10 @@ void main() { greetingLabel: greetingLabel, onResetPassTapped: () {}, onSignupTapped: () {}, - onLoginError: (_) {}, onLoggedIn: (_) {}, onSocialAuthButtonPressed: (p0) {}, + onLoginError: (_) {}, + authErrorStateHandler: MockAuthStateErrorHandler(), ), ), ); @@ -72,9 +75,10 @@ void main() { greetingLabel: greetingLabel, onResetPassTapped: () {}, onSignupTapped: () {}, - onLoginError: (_) {}, onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) {}, + onLoginError: (_) {}, + authErrorStateHandler: MockAuthStateErrorHandler(), ), ), ); @@ -105,9 +109,10 @@ void main() { greetingLabel: greetingLabel, onResetPassTapped: () {}, onSignupTapped: () {}, - onLoginError: (_) {}, onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) {}, + onLoginError: (_) {}, + authErrorStateHandler: MockAuthStateErrorHandler(), ), )); @@ -134,9 +139,10 @@ void main() { onSignupTapped: () { onSignupTappedCalled = true; }, - onLoginError: (_) {}, onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) {}, + onLoginError: (_) {}, + authErrorStateHandler: MockAuthStateErrorHandler(), ), )); @@ -170,11 +176,12 @@ void main() { greetingLabel: greetingLabel, onResetPassTapped: () {}, onSignupTapped: () {}, - onLoginError: (_) {}, onLoggedIn: (_) { onLoggedInCalled = true; }, onSocialAuthButtonPressed: (_) {}, + onLoginError: (_) {}, + authErrorStateHandler: MockAuthStateErrorHandler(), ), )); @@ -208,6 +215,7 @@ void main() { }, onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) {}, + authErrorStateHandler: MockAuthStateErrorHandler(), ), )); @@ -234,9 +242,10 @@ void main() { onResetPassTappedCalled = true; }, onSignupTapped: () {}, - onLoginError: (_) {}, onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) {}, + onLoginError: (_) {}, + authErrorStateHandler: MockAuthStateErrorHandler(), ), )); @@ -264,11 +273,12 @@ void main() { greetingLabel: greetingLabel, onResetPassTapped: () {}, onSignupTapped: () {}, - onLoginError: (_) {}, onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) { onSocialAuthButtonPressedCalled = true; }, + onLoginError: (_) {}, + authErrorStateHandler: MockAuthStateErrorHandler(), ), )); diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart index 2da333238..321b534f3 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart @@ -16,33 +16,44 @@ import '../../../pump_app.dart'; void main() { group('DerivSignupLayout', () { late MockSignupCubit signupCubit; + late MockAuthCubit authCubit; const String signupPageLabel = 'Create a free account'; const String signupPageDescription = 'Start trading within minutes.'; setUpAll(() { signupCubit = MockSignupCubit(); + authCubit = MockAuthCubit(); when(() => signupCubit.state) .thenAnswer((_) => const DerivSignupInitialState()); when(() => signupCubit.stream).thenAnswer( (_) => Stream.fromIterable([const DerivSignupInitialState()])); + + when(() => authCubit.state).thenAnswer((_) => DerivAuthLoadingState()); + + when(() => authCubit.stream) + .thenAnswer((_) => Stream.fromIterable([DerivAuthLoadingState()])); }); patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp( settle: false, - BlocProvider.value( - value: signupCubit, - child: DerivSignupLayout( - signupPageLabel: signupPageLabel, - signupPageDescription: signupPageDescription, - onSocialAuthButtonPressed: (_) {}, - onSingupError: (_) {}, - onSingupEmailSent: (_) {}, - onSignupPressed: () {}, - onLoginTapped: () {}, + BlocProvider.value( + value: authCubit, + child: BlocProvider.value( + value: signupCubit, + child: DerivSignupLayout( + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) {}, + onSingupEmailSent: (_) {}, + onSignupPressed: () {}, + onLoginTapped: () {}, + authErrorStateHandler: MockAuthErrorStateHandler(), + ), ), )); @@ -57,18 +68,22 @@ void main() { (PatrolTester $) async { bool isOnSocialAuthButtonPressedCalled = false; - await $.pumpApp(BlocProvider.value( - value: signupCubit, - child: DerivSignupLayout( - signupPageLabel: signupPageLabel, - signupPageDescription: signupPageDescription, - onSocialAuthButtonPressed: (_) { - isOnSocialAuthButtonPressedCalled = true; - }, - onSingupError: (_) {}, - onSingupEmailSent: (_) {}, - onSignupPressed: () {}, - onLoginTapped: () {}, + await $.pumpApp(BlocProvider.value( + value: authCubit, + child: BlocProvider.value( + value: signupCubit, + child: DerivSignupLayout( + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) { + isOnSocialAuthButtonPressedCalled = true; + }, + onSingupError: (_) {}, + onSingupEmailSent: (_) {}, + onSignupPressed: () {}, + onLoginTapped: () {}, + authErrorStateHandler: MockAuthErrorStateHandler(), + ), ), )); @@ -87,16 +102,20 @@ void main() { when(() => signupCubit.stream).thenAnswer( (_) => Stream.fromIterable([const DerivSignupEmailSentState()])); - await $.pumpApp(BlocProvider.value( - value: signupCubit, - child: DerivSignupLayout( - signupPageLabel: signupPageLabel, - signupPageDescription: signupPageDescription, - onSocialAuthButtonPressed: (_) {}, - onSingupError: (_) {}, - onSingupEmailSent: (_) => isOnSignupEmailSentCalled = true, - onSignupPressed: () {}, - onLoginTapped: () {}, + await $.pumpApp(BlocProvider.value( + value: authCubit, + child: BlocProvider.value( + value: signupCubit, + child: DerivSignupLayout( + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) {}, + onSingupEmailSent: (_) => isOnSignupEmailSentCalled = true, + onSignupPressed: () {}, + onLoginTapped: () {}, + authErrorStateHandler: MockAuthErrorStateHandler(), + ), ), )); @@ -110,16 +129,20 @@ void main() { when(() => signupCubit.sendVerificationEmail('test@gmail.com')) .thenAnswer((_) async => const DerivSignupEmailSentState()); - await $.pumpApp(BlocProvider.value( - value: signupCubit, - child: DerivSignupLayout( - signupPageLabel: signupPageLabel, - signupPageDescription: signupPageDescription, - onSocialAuthButtonPressed: (_) {}, - onSingupError: (_) {}, - onSingupEmailSent: (_) {}, - onSignupPressed: () => isOnSignupPressedCalled = true, - onLoginTapped: () {}, + await $.pumpApp(BlocProvider.value( + value: authCubit, + child: BlocProvider.value( + value: signupCubit, + child: DerivSignupLayout( + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) {}, + onSingupEmailSent: (_) {}, + onSignupPressed: () => isOnSignupPressedCalled = true, + onLoginTapped: () {}, + authErrorStateHandler: MockAuthErrorStateHandler(), + ), ), )); @@ -138,18 +161,22 @@ void main() { (PatrolTester $) async { bool isOnLoginTappedCalled = false; - await $.pumpApp(BlocProvider.value( - value: signupCubit, - child: DerivSignupLayout( - signupPageLabel: signupPageLabel, - signupPageDescription: signupPageDescription, - onSocialAuthButtonPressed: (_) {}, - onSingupError: (_) {}, - onSingupEmailSent: (_) {}, - onSignupPressed: () {}, - onLoginTapped: () { - isOnLoginTappedCalled = true; - }, + await $.pumpApp(BlocProvider.value( + value: authCubit, + child: BlocProvider.value( + value: signupCubit, + child: DerivSignupLayout( + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) {}, + onSingupEmailSent: (_) {}, + onSignupPressed: () {}, + onLoginTapped: () { + isOnLoginTappedCalled = true; + }, + authErrorStateHandler: MockAuthErrorStateHandler(), + ), ), )); @@ -171,16 +198,20 @@ void main() { when(() => signupCubit.stream).thenAnswer( (_) => Stream.fromIterable([const DerivSignupErrorState('')])); - await $.pumpApp(BlocProvider.value( - value: signupCubit, - child: DerivSignupLayout( - signupPageLabel: signupPageLabel, - signupPageDescription: signupPageDescription, - onSocialAuthButtonPressed: (_) {}, - onSingupError: (_) => isOnSignupErrorCalled = true, - onSingupEmailSent: (_) {}, - onSignupPressed: () {}, - onLoginTapped: () {}, + await $.pumpApp(BlocProvider.value( + value: authCubit, + child: BlocProvider.value( + value: signupCubit, + child: DerivSignupLayout( + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) => isOnSignupErrorCalled = true, + onSingupEmailSent: (_) {}, + onSignupPressed: () {}, + onLoginTapped: () {}, + authErrorStateHandler: MockAuthErrorStateHandler(), + ), ), )); diff --git a/packages/deriv_auth_ui/test/mocks.dart b/packages/deriv_auth_ui/test/mocks.dart index ca42e2da0..3429c7e51 100644 --- a/packages/deriv_auth_ui/test/mocks.dart +++ b/packages/deriv_auth_ui/test/mocks.dart @@ -1,4 +1,5 @@ import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:mocktail/mocktail.dart'; class MockAuthCubit extends Mock implements DerivAuthCubit {} @@ -6,3 +7,5 @@ class MockAuthCubit extends Mock implements DerivAuthCubit {} class MockDerivResetPassCubit extends Mock implements DerivResetPassCubit {} class MockSignupCubit extends Mock implements DerivSignupCubit {} + +class MockAuthErrorStateHandler extends Mock implements AuthErrorStateHandler {} From 737ed84a282869bce1bbde00794309e9e9fc2b34 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Thu, 26 Oct 2023 19:02:27 +0800 Subject: [PATCH 07/44] feat: add auth error state handler in set password page --- .../signup/pages/set_password_page.dart | 12 +------ .../layouts/deriv_set_password_layout.dart | 19 +++++++--- .../deriv_set_password_layout_test.dart | 35 +++---------------- 3 files changed, 20 insertions(+), 46 deletions(-) diff --git a/packages/deriv_auth_ui/example/lib/features/signup/pages/set_password_page.dart b/packages/deriv_auth_ui/example/lib/features/signup/pages/set_password_page.dart index e009e632e..0dd3acec9 100644 --- a/packages/deriv_auth_ui/example/lib/features/signup/pages/set_password_page.dart +++ b/packages/deriv_auth_ui/example/lib/features/signup/pages/set_password_page.dart @@ -1,6 +1,5 @@ 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:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -12,16 +11,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 route) => false, - ); - } - }, + authErrorStateHandler: DefaultAuthErrorStateHandler(context: context), onDerivSignupState: (context, state) { if (state is DerivSignupDoneState) { context diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart index dc4469142..0850b9ee7 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart @@ -1,8 +1,8 @@ import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; import 'package:deriv_auth_ui/src/core/helpers/assets.dart'; -import 'package:deriv_auth_ui/src/features/signup/models/deriv_auth_utm_model.dart'; import 'package:deriv_auth_ui/src/features/signup/widgets/password_policy_checker_widget.dart'; import 'package:deriv_theme/deriv_theme.dart'; import 'package:deriv_ui/deriv_ui.dart'; @@ -14,11 +14,11 @@ import 'package:flutter_svg/flutter_svg.dart'; class DerivSetPasswordLayout extends StatefulWidget { /// constructor of country set password page const DerivSetPasswordLayout({ - required this.onDerivAuthState, required this.onDerivSignupState, required this.onPreviousPressed, required this.verificationCode, required this.residence, + required this.authErrorStateHandler, this.utmModel, Key? key, }) : super(key: key); @@ -32,8 +32,8 @@ class DerivSetPasswordLayout extends StatefulWidget { /// Utm model final DerivAuthUtmModel? utmModel; - /// Callback to be called when auth state changes. - final void Function(BuildContext, DerivAuthState) onDerivAuthState; + /// Implementation of [AuthErrorStateHandler]. + final AuthErrorStateHandler authErrorStateHandler; /// Callback to be called when signup state changes. final void Function(BuildContext, DerivSignupState) onDerivSignupState; @@ -65,7 +65,7 @@ class _DerivSetPasswordLayoutState extends State { @override Widget build(BuildContext context) => BlocListener( - listener: widget.onDerivAuthState, + listener: _onAuthState, child: Scaffold( backgroundColor: context.theme.colors.primary, body: SafeArea( @@ -207,4 +207,13 @@ class _DerivSetPasswordLayoutState extends State { utmAdId: widget.utmModel?.utmAdId, ); } + + void _onAuthState(BuildContext _, DerivAuthState state) { + if (state is DerivAuthErrorState) { + authErrorStateMapper( + authErrorState: state, + authErrorStateHandler: widget.authErrorStateHandler, + ); + } + } } diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart index af45d1750..45a2a588e 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart @@ -40,7 +40,7 @@ void main() { BlocProvider.value(value: signupCubit), ], child: DerivSetPasswordLayout( - onDerivAuthState: (_, __) {}, + authErrorStateHandler: MockAuthErrorStateHandler(), onDerivSignupState: (_, __) {}, onPreviousPressed: () {}, verificationCode: '123456', @@ -53,31 +53,6 @@ void main() { expect($(ElevatedButton), findsNWidgets(2)); }); - patrolWidgetTest('onDerivAuthState is called on DerivAuth state changes', - (PatrolTester $) async { - bool isOnDerivAuthStateCalled = false; - - when(() => authCubit.stream).thenAnswer((_) => Stream.fromIterable([ - DerivAuthLoadingState(), - DerivAuthLoggedOutState(), - ])); - - await $.pumpApp(MultiBlocProvider( - providers: [ - BlocProvider.value(value: authCubit), - BlocProvider.value(value: signupCubit), - ], - child: DerivSetPasswordLayout( - onDerivAuthState: (_, __) => isOnDerivAuthStateCalled = true, - onDerivSignupState: (_, __) {}, - onPreviousPressed: () {}, - verificationCode: 'verificationCode', - residence: 'residence'), - )); - - expect(isOnDerivAuthStateCalled, true); - }); - patrolWidgetTest( 'onDerivSignupState is called on DerivSignup state changes', (PatrolTester $) async { @@ -96,7 +71,7 @@ void main() { BlocProvider.value(value: signupCubit), ], child: DerivSetPasswordLayout( - onDerivAuthState: (_, __) {}, + authErrorStateHandler: MockAuthErrorStateHandler(), onDerivSignupState: (_, __) => isOnDerivSignupStateCalled = true, onPreviousPressed: () {}, @@ -119,7 +94,7 @@ void main() { BlocProvider.value(value: signupCubit), ], child: DerivSetPasswordLayout( - onDerivAuthState: (_, __) {}, + authErrorStateHandler: MockAuthErrorStateHandler(), onDerivSignupState: (_, __) {}, onPreviousPressed: () => isOnPreviousPressedCalled = true, verificationCode: 'verificationCode', @@ -142,7 +117,7 @@ void main() { BlocProvider.value(value: signupCubit), ], child: DerivSetPasswordLayout( - onDerivAuthState: (_, __) {}, + authErrorStateHandler: MockAuthErrorStateHandler(), onDerivSignupState: (_, __) {}, onPreviousPressed: () {}, verificationCode: 'verificationCode', @@ -172,7 +147,7 @@ void main() { BlocProvider.value(value: signupCubit), ], child: DerivSetPasswordLayout( - onDerivAuthState: (_, __) {}, + authErrorStateHandler: MockAuthErrorStateHandler(), onDerivSignupState: (_, __) {}, onPreviousPressed: () {}, verificationCode: 'verificationCode', From 05c12d53906de229d316fdbcf18396e4b34aed1f Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:05:44 +0800 Subject: [PATCH 08/44] refactor: auth state handling in signup and set password page --- .../src/core/states/auth_state_listener.dart | 63 +++++++++++++++++++ .../layouts/deriv_set_password_layout.dart | 20 +++--- .../signup/layouts/deriv_signup_layout.dart | 20 +++--- 3 files changed, 80 insertions(+), 23 deletions(-) create mode 100644 packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart diff --git a/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart b/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart new file mode 100644 index 000000000..5a308065c --- /dev/null +++ b/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart @@ -0,0 +1,63 @@ +import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth_ui/deriv_auth_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// {@template auth_state_listener} +/// A [Widget] that listens to the [DerivAuthCubit] state changes. +/// This was created to make it easier to use [AuthErrorStateHandler]. +/// {@endtemplate} +class AuthStateListener extends StatelessWidget { + /// {@macro auth_state_listener} + const AuthStateListener({ + required this.child, + super.key, + this.onLoggedIn, + this.onLoggedOut, + this.onLoading, + this.onError, + this.authErrorStateHandler, + }); + + /// The [Widget] that is using this [AuthStateListener]. + 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; + + /// Implementation of [AuthErrorStateHandler]. + final AuthErrorStateHandler? authErrorStateHandler; + + @override + Widget build(BuildContext context) => + BlocListener( + 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); + + if (authErrorStateHandler != null) { + authErrorStateMapper( + authErrorState: state, + authErrorStateHandler: authErrorStateHandler!, + ); + } + } + }, + child: child, + ); +} diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart index 0850b9ee7..36d1d11f4 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart @@ -3,6 +3,7 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; import 'package:deriv_auth_ui/src/core/helpers/assets.dart'; +import 'package:deriv_auth_ui/src/core/states/auth_state_listener.dart'; import 'package:deriv_auth_ui/src/features/signup/widgets/password_policy_checker_widget.dart'; import 'package:deriv_theme/deriv_theme.dart'; import 'package:deriv_ui/deriv_ui.dart'; @@ -20,6 +21,7 @@ class DerivSetPasswordLayout extends StatefulWidget { required this.residence, required this.authErrorStateHandler, this.utmModel, + this.onAuthError, Key? key, }) : super(key: key); @@ -35,6 +37,9 @@ class DerivSetPasswordLayout extends StatefulWidget { /// Implementation of [AuthErrorStateHandler]. final AuthErrorStateHandler authErrorStateHandler; + /// Callback to be called on [DerivAuthErrorState] + final Function(DerivAuthErrorState)? onAuthError; + /// Callback to be called when signup state changes. final void Function(BuildContext, DerivSignupState) onDerivSignupState; @@ -63,9 +68,9 @@ class _DerivSetPasswordLayoutState extends State { } @override - Widget build(BuildContext context) => - BlocListener( - listener: _onAuthState, + Widget build(BuildContext context) => AuthStateListener( + authErrorStateHandler: widget.authErrorStateHandler, + onError: widget.onAuthError, child: Scaffold( backgroundColor: context.theme.colors.primary, body: SafeArea( @@ -207,13 +212,4 @@ class _DerivSetPasswordLayoutState extends State { utmAdId: widget.utmModel?.utmAdId, ); } - - void _onAuthState(BuildContext _, DerivAuthState state) { - if (state is DerivAuthErrorState) { - authErrorStateMapper( - authErrorState: state, - authErrorStateHandler: widget.authErrorStateHandler, - ); - } - } } diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart index dbdc359d5..b09eeee4a 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart @@ -2,6 +2,7 @@ import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; +import 'package:deriv_auth_ui/src/core/states/auth_state_listener.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_divider.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_panel.dart'; import 'package:deriv_theme/deriv_theme.dart'; @@ -24,6 +25,7 @@ class DerivSignupLayout extends StatefulWidget { required this.signupPageDescription, required this.authErrorStateHandler, this.enableReferralSection = true, + this.onAuthError, Key? key, }) : super(key: key); @@ -36,6 +38,10 @@ class DerivSignupLayout extends StatefulWidget { /// Implementation of [AuthErrorStateHandler]. final AuthErrorStateHandler authErrorStateHandler; + /// Callback to be called on [DerivAuthErrorState]. + /// Useful if needed to do anything additional to [authErrorStateHandler]. + final Function(DerivAuthErrorState)? onAuthError; + /// Callback to be called when signup email is sent. final Function(String) onSingupEmailSent; @@ -82,8 +88,9 @@ class _DerivSignupLayoutState extends State { Text(context.localization.labelSignUp, style: TextStyles.title), backgroundColor: context.theme.colors.secondary, ), - body: BlocListener( - listener: _onAuthState, + body: AuthStateListener( + authErrorStateHandler: widget.authErrorStateHandler, + onError: widget.onAuthError, child: BlocConsumer( listener: _onSignUpState, builder: (BuildContext context, DerivSignupState state) => Form( @@ -307,15 +314,6 @@ class _DerivSignupLayoutState extends State { } } - void _onAuthState(BuildContext _, DerivAuthState state) { - if (state is DerivAuthErrorState) { - authErrorStateMapper( - authErrorState: state, - authErrorStateHandler: widget.authErrorStateHandler, - ); - } - } - bool _isFormValid() => _emailValidator(emailController.text) == null && _referralValidator(referralController.text) == null; From f8f9517127a5cda26eed12c290f702c8623826e9 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:25:45 +0800 Subject: [PATCH 09/44] feat: add social button flag --- .../widgets/deriv_social_auth_panel.dart | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_panel.dart b/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_panel.dart index 91298a3d4..6100b7bbf 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_panel.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_panel.dart @@ -10,6 +10,7 @@ class DerivSocialAuthPanel extends StatelessWidget { const DerivSocialAuthPanel({ required this.onSocialAuthButtonPressed, this.isEnabled = true, + this.isVisible = true, Key? key, }) : super(key: key); @@ -19,19 +20,26 @@ class DerivSocialAuthPanel extends StatelessWidget { /// Defaults to `true`. final bool isEnabled; + /// Whether the buttons are visible. + /// Defaults to `true`. Acts as a flag to hide the buttons. + final bool isVisible; + /// onPressed callback for social auth buttons. final void Function(SocialAuthProvider) onSocialAuthButtonPressed; @override - Widget build(BuildContext context) => Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildSocialAuthButton(SocialAuthProvider.apple), - const SizedBox(width: ThemeProvider.margin24), - _buildSocialAuthButton(SocialAuthProvider.google), - const SizedBox(width: ThemeProvider.margin24), - _buildSocialAuthButton(SocialAuthProvider.facebook), - ], + Widget build(BuildContext context) => Visibility( + visible: isVisible, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildSocialAuthButton(SocialAuthProvider.apple), + const SizedBox(width: ThemeProvider.margin24), + _buildSocialAuthButton(SocialAuthProvider.google), + const SizedBox(width: ThemeProvider.margin24), + _buildSocialAuthButton(SocialAuthProvider.facebook), + ], + ), ); Widget _buildSocialAuthButton(SocialAuthProvider socialAuthProvider) => From 7cc239455f1169777f8c11d78487f1e39e35e76b Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Mon, 30 Oct 2023 15:32:13 +0800 Subject: [PATCH 10/44] feat: country consent added in country_selection_layout --- .../lib/generated/intl/messages_en.dart | 2 + .../deriv_auth_ui/lib/generated/l10n.dart | 10 + packages/deriv_auth_ui/lib/l10n/intl_en.arb | 3 +- .../helpers/country_selection_helper.dart | 86 +++++++++ .../cubits/deriv_country_selection_cubit.dart | 78 ++++---- .../cubits/deriv_country_selection_state.dart | 70 +++++-- .../deriv_country_selection_layout.dart | 171 +++++++++++++++--- .../signup/models/deriv_residence_model.dart | 4 + .../deriv_country_selection_cubit_test.dart | 2 +- .../deriv_country_selection_state_test.dart | 2 +- .../presentation/widgets/custom_checkbox.dart | 17 +- 11 files changed, 360 insertions(+), 85 deletions(-) create mode 100644 packages/deriv_auth_ui/lib/src/core/helpers/country_selection_helper.dart diff --git a/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart b/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart index 2a10c67ce..898c7978e 100644 --- a/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart +++ b/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart @@ -111,6 +111,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Choose country"), "labelChooseNewPass": MessageLookupByLibrary.simpleMessage("Choose a new password"), + "labelCountryConsentBrazil": MessageLookupByLibrary.simpleMessage( + "I hereby confirm that my request for opening an account with Deriv to trade OTC products issued and offered exclusively outside Brazil was initiated by me. I fully understand that Deriv is not regulated by CVM and by approaching Deriv I intend to set up a relation with a foreign company."), "labelCreatePass": MessageLookupByLibrary.simpleMessage("Create a password"), "labelCreatePassword": diff --git a/packages/deriv_auth_ui/lib/generated/l10n.dart b/packages/deriv_auth_ui/lib/generated/l10n.dart index 0028dc351..d71b0d79d 100644 --- a/packages/deriv_auth_ui/lib/generated/l10n.dart +++ b/packages/deriv_auth_ui/lib/generated/l10n.dart @@ -810,6 +810,16 @@ class DerivAuthUILocalization { args: [], ); } + + /// `I hereby confirm that my request for opening an account with Deriv to trade OTC products issued and offered exclusively outside Brazil was initiated by me. I fully understand that Deriv is not regulated by CVM and by approaching Deriv I intend to set up a relation with a foreign company.` + String get labelCountryConsentBrazil { + return Intl.message( + 'I hereby confirm that my request for opening an account with Deriv to trade OTC products issued and offered exclusively outside Brazil was initiated by me. I fully understand that Deriv is not regulated by CVM and by approaching Deriv I intend to set up a relation with a foreign company.', + name: 'labelCountryConsentBrazil', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate diff --git a/packages/deriv_auth_ui/lib/l10n/intl_en.arb b/packages/deriv_auth_ui/lib/l10n/intl_en.arb index 16389d679..ce4590c35 100644 --- a/packages/deriv_auth_ui/lib/l10n/intl_en.arb +++ b/packages/deriv_auth_ui/lib/l10n/intl_en.arb @@ -101,5 +101,6 @@ "informSelfClosed": "Your account has been closed.", "informUnexpectedError": "An unexpected error occurred.", "informUnsupportedCountry": "Your country is not supported.", - "informExpiredAccount": "Your account is expired" + "informExpiredAccount": "Your account is expired", + "labelCountryConsentBrazil": "I hereby confirm that my request for opening an account with Deriv to trade OTC products issued and offered exclusively outside Brazil was initiated by me. I fully understand that Deriv is not regulated by CVM and by approaching Deriv I intend to set up a relation with a foreign company." } \ No newline at end of file diff --git a/packages/deriv_auth_ui/lib/src/core/helpers/country_selection_helper.dart b/packages/deriv_auth_ui/lib/src/core/helpers/country_selection_helper.dart new file mode 100644 index 000000000..5af80cb6a --- /dev/null +++ b/packages/deriv_auth_ui/lib/src/core/helpers/country_selection_helper.dart @@ -0,0 +1,86 @@ +import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; +import 'package:flutter/material.dart'; + +/// Returns `true` if the country is not in the [notAllowedCountryCodes] set, +/// otherwise returns `false`. +bool isAllowedCountry({required String? countryCode}) => + !notAllowedCountryCodes.contains(countryCode); + +/// Returns `true` if consent is required for the given [countryCode], +/// otherwise returns `false`. +bool isConsentRequired({required String? countryCode}) { + if (countryCode == null) { + return false; + } + + return countriesRequiringConsent.contains(countryCode.toLowerCase()); +} + +/// Retrieves the consent message for a given country code. +/// +/// If the country requires consent, the corresponding consent message is returned. +/// Otherwise, an empty string is returned. +/// +/// When a new country is added to the set of [countriesRequiringConsent], +/// update the [consentMessages] map. +String getCountryConsentMessage( + BuildContext context, { + required String? countryCode, +}) { + if (countryCode == null) { + return ''; + } + + final Map consentMessages = { + 'br': context.localization.labelCountryConsentBrazil, + // Add more countries and consent messages here. + }; + + return consentMessages[countryCode.toLowerCase()] ?? ''; +} + +/// Set of country codes that are not allowed to create an account. +const Set notAllowedCountryCodes = { + // MF country codes. + 'de', + 'es', + 'fr', + 'gr', + 'it', + 'lu', + 'mf', + // MLT country codes. + 'at', + 'be', + 'bg', + 'cy', + 'cz', + 'dk', + 'ee', + 'fi', + 'hr', + 'hu', + 'ie', + 'lt', + 'lv', + 'nl', + 'pl', + 'pt', + 'ro', + 'se', + 'si', + 'sk', + // MX country codes. + 'gb', + 'im', +}; + +/// [countriesRequiringConsent] is a set of country codes that are required +/// to show consent. +/// +/// If any country is required to show consent, add the country code +/// in lowercase to the set. +const Set countriesRequiringConsent = { + 'br', + // Add countries here. +}; diff --git a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart index 4ce13603a..355db8bd5 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart @@ -1,4 +1,4 @@ -import 'package:equatable/equatable.dart'; +import 'package:deriv_auth_ui/src/core/helpers/country_selection_helper.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:deriv_auth_ui/src/features/signup/models/deriv_residence_model.dart'; @@ -14,53 +14,53 @@ class DerivCountrySelectionCubit extends Cubit { final Future> residences; /// Fetches residence countries. - Future fetchResidenceCounties() async { + Future fetchResidenceCountries() async { final List countries = await residences; final List filteredCountries = countries .where( - (DerivResidenceModel country) => _isAllowedCountry(country), + (DerivResidenceModel country) => + isAllowedCountry(countryCode: country.value), ) .toList(); emit(DerivCountrySelectionLoadedState(filteredCountries)); } - bool _isAllowedCountry(DerivResidenceModel country) => _notAllowedCountryCodes - .every((String countryCode) => country.code != countryCode); + /// Changes the selected country and updates the state accordingly. + /// + /// If [selectedCountry] is not `null`, this method emits a [DerivCountryChangedState] + /// with the updated selected country. + /// + /// This method does not perform any action if [selectedCountry] is `null`. + Future changeSelectedCountry({ + DerivResidenceModel? selectedCountry, + }) async { + if (selectedCountry != null) { + emit( + DerivCountryChangedState( + state.countries, + selectedCountry: selectedCountry, + selectedCountryRequiresConsent: isConsentRequired( + countryCode: selectedCountry.value, + ), + ), + ); + } + } - static final List _notAllowedCountryCodes = [ - // MF country codes. - 'de', - 'es', - 'fr', - 'gr', - 'it', - 'lu', - 'mf', - // MLT country codes. - 'at', - 'be', - 'bg', - 'cy', - 'cz', - 'dk', - 'ee', - 'fi', - 'hr', - 'hu', - 'ie', - 'lt', - 'lv', - 'nl', - 'pl', - 'pt', - 'ro', - 'se', - 'si', - 'sk', - // MX country codes. - 'gb', - 'im' - ]; + /// Updates the country consent status and triggers a state change. + /// + /// [agreedToTerms]: Whether the user has agreed to the terms for the + /// selected country. + Future updateCountryConsentStatus({bool? agreedToTerms = false}) async { + emit( + DerivCountryConsentChangedState( + state.countries, + selectedCountry: state.selectedCountry, + selectedCountryRequiresConsent: state.selectedCountryRequiresConsent, + agreedToTerms: agreedToTerms, + ), + ); + } } diff --git a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart index 6f67179df..63f553b0e 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart @@ -1,30 +1,76 @@ part of 'deriv_country_selection_cubit.dart'; /// Country selection state -abstract class DerivCountrySelectionState extends Equatable { +abstract class DerivCountrySelectionState { /// Initialize country selection state. - const DerivCountrySelectionState(this.countries); + const DerivCountrySelectionState( + this.countries, { + this.selectedCountry, + this.agreedToTerms = false, + this.selectedCountryRequiresConsent = false, + }); /// List of countries. - final List countries; + final List? countries; + + /// Selected country. + final DerivResidenceModel? selectedCountry; + + /// If the selected country requires consent, value must be true to continue. + /// Default is null. + /// Example: For Brazil, Brazil requires consent to continue. + /// The user must agree to the terms to continue. + final bool agreedToTerms; + + /// If the selected country requires consent to continue, value is true. + /// Default is false. + final bool selectedCountryRequiresConsent; } /// Initial state. class DerivCountrySelectionInitialState extends DerivCountrySelectionState { - /// Initializes initial state. + /// Initialises initial state. const DerivCountrySelectionInitialState() : super(const []); - - @override - List get props => []; } /// Country list loaded state. class DerivCountrySelectionLoadedState extends DerivCountrySelectionState { - /// Initialize country list loaded state - const DerivCountrySelectionLoadedState(List countries) - : super(countries); + /// Initialise country list loaded state + const DerivCountrySelectionLoadedState( + List? countries, { + DerivResidenceModel? selectedCountry, + }) : super(countries, selectedCountry: selectedCountry); +} + +/// Country selection changed state. +class DerivCountryChangedState extends DerivCountrySelectionState { + /// Initialise country selection changed state. + const DerivCountryChangedState( + List? countries, { + DerivResidenceModel? selectedCountry, + bool? selectedCountryRequiresConsent, + }) : super( + countries, + selectedCountry: selectedCountry, + selectedCountryRequiresConsent: + selectedCountryRequiresConsent ?? false, + ); +} - @override - List get props => [countries]; +/// State to update country selection consent. +class DerivCountryConsentChangedState extends DerivCountrySelectionState { + /// Initialize country list loaded state + const DerivCountryConsentChangedState( + List? countries, { + DerivResidenceModel? selectedCountry, + bool? selectedCountryRequiresConsent, + bool? agreedToTerms, + }) : super( + countries, + selectedCountry: selectedCountry, + agreedToTerms: agreedToTerms ?? false, + selectedCountryRequiresConsent: + selectedCountryRequiresConsent ?? false, + ); } diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart index 01ccde127..2c931576c 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart @@ -1,5 +1,6 @@ import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/helpers/assets.dart'; +import 'package:deriv_auth_ui/src/core/helpers/country_selection_helper.dart'; import 'package:deriv_auth_ui/src/features/signup/cubits/deriv_country_selection_cubit.dart'; import 'package:deriv_auth_ui/src/features/signup/models/deriv_residence_model.dart'; import 'package:deriv_auth_ui/src/features/signup/widgets/country_selection_list_widget.dart'; @@ -17,6 +18,7 @@ class DerivCountrySelectionLayout extends StatefulWidget { required this.residences, required this.onNextPressed, this.affiliateToken, + this.countryConsentMessage, Key? key, }) : super(key: key); @@ -32,6 +34,9 @@ class DerivCountrySelectionLayout extends StatefulWidget { /// Affiliate token. final String? affiliateToken; + /// Message to be shown beside country consent checkbox. + final String? countryConsentMessage; + @override State createState() => _DerivCountrySelectionLayoutState(); @@ -45,14 +50,13 @@ class _DerivCountrySelectionLayoutState late TextEditingController _textController; late final DerivCountrySelectionCubit _countrySelectionCubit; - DerivResidenceModel? _selectedResidence; @override void initState() { super.initState(); _countrySelectionCubit = DerivCountrySelectionCubit(widget.residences) - ..fetchResidenceCounties(); + ..fetchResidenceCountries(); _textController = TextEditingController(); } @@ -61,15 +65,29 @@ class _DerivCountrySelectionLayoutState Widget build(BuildContext context) => Scaffold( backgroundColor: context.theme.colors.primary, body: SafeArea( - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Expanded(child: _buildUpperPage()), - Padding( - padding: const EdgeInsets.all(ThemeProvider.margin16), - child: _buildLowerPage(), - ), - ], + child: BlocListener( + bloc: _countrySelectionCubit, + listener: (BuildContext context, DerivCountrySelectionState state) { + if (state.selectedCountry != null && + state.selectedCountry?.name != null) { + _textController.text = state.selectedCountry?.name ?? ''; + } + + WidgetsBinding.instance.addPostFrameCallback((_) { + _formKey.currentState!.validate(); + }); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Expanded(child: _buildUpperPage()), + Padding( + padding: const EdgeInsets.all(ThemeProvider.margin16), + child: _buildLowerPage(), + ), + ], + ), ), ), ); @@ -86,7 +104,11 @@ class _DerivCountrySelectionLayoutState style: TextStyles.title, ), const SizedBox(height: ThemeProvider.margin24), - _buildSelectionInput() + _buildSelectionInput(), + const SizedBox(height: ThemeProvider.margin16), + _buildCountryConsentCheckbox( + countryConsentMessage: widget.countryConsentMessage, + ), ], ), ); @@ -114,24 +136,32 @@ class _DerivCountrySelectionLayoutState color: context.theme.colors.prominent, ), readOnly: true, - enabled: state is DerivCountrySelectionLoadedState, - validator: (String? value) => _selectedResidence!.isDisabled - ? context.localization.warnCountryNotAvailable - : null, - onTap: () => _onSelectCountryTap(state.countries), + enabled: _shouldEnableCountrySelectionField(state), + validator: (String? value) => _countrySelectionValidator(context, + selectedCountry: state.selectedCountry), + onTap: () => + _onSelectCountryTap(state.countries ?? []), ), ), ); - Widget _buildNextButton() => PrimaryButton( - isEnabled: - _selectedResidence != null && !_selectedResidence!.isDisabled, - onPressed: widget.onNextPressed, - child: Center( - child: Text( - context.localization.actionNext, - style: TextStyles.button - .copyWith(color: context.theme.colors.prominent), + Widget _buildNextButton() => + BlocBuilder( + bloc: _countrySelectionCubit, + builder: (BuildContext context, DerivCountrySelectionState state) => + PrimaryButton( + isEnabled: _shouldEnableNextButton( + state.selectedCountry, + isConsentRequired: state.selectedCountryRequiresConsent, + agreedToTerms: state.agreedToTerms, + ), + onPressed: widget.onNextPressed, + child: Center( + child: Text( + context.localization.actionNext, + style: TextStyles.button + .copyWith(color: context.theme.colors.prominent), + ), ), ), ); @@ -150,10 +180,9 @@ class _DerivCountrySelectionLayoutState countries: countries, onChanged: (int index) => setState( () { - _textController.text = countries[index].name; - _selectedResidence = countries[index]; - - _formKey.currentState!.validate(); + _countrySelectionCubit.changeSelectedCountry( + selectedCountry: countries[index], + ); }, ), ), @@ -161,6 +190,88 @@ class _DerivCountrySelectionLayoutState ); } + Widget _buildCountryConsentCheckbox({String? countryConsentMessage}) => + BlocBuilder( + bloc: _countrySelectionCubit, + builder: (BuildContext context, DerivCountrySelectionState state) { + final DerivResidenceModel? selectedCountry = state.selectedCountry; + + if (selectedCountry == null || + !state.selectedCountryRequiresConsent) { + return const SizedBox.shrink(); + } + + return CustomCheckbox( + padding: const EdgeInsets.symmetric( + vertical: ThemeProvider.margin16, + ), + contentsVerticalAlignment: CrossAxisAlignment.start, + value: state.agreedToTerms, + onValueChanged: ({bool? isChecked}) => + _countrySelectionCubit.updateCountryConsentStatus( + agreedToTerms: isChecked, + ), + message: countryConsentMessage ?? + getCountryConsentMessage( + context, + countryCode: selectedCountry.value, + ), + ); + }, + ); + + /// Validates the country selection. + String? _countrySelectionValidator( + BuildContext context, { + DerivResidenceModel? selectedCountry, + }) { + if (selectedCountry != null && selectedCountry.isDisabled) { + return context.localization.warnCountryNotAvailable; + } + + return null; + } + + /// Determines whether the next button should be enabled based on country selection. + /// + /// Returns `true` if the following conditions are met: + /// - A country is selected. + /// - The selected country is not disabled. + /// - If `isConsentRequired` is true, the user must agree to the terms. + /// + /// Otherwise, returns `false`. + bool _shouldEnableNextButton( + DerivResidenceModel? selectedCountry, { + bool agreedToTerms = false, + bool isConsentRequired = false, + }) { + if (selectedCountry == null) { + return false; + } + + bool shouldEnable = !selectedCountry.isDisabled; + + if (isConsentRequired) { + shouldEnable = shouldEnable && agreedToTerms; + } + + return shouldEnable; + } + + /// Determines whether the country selection field should be enabled + /// based on the given state. + bool _shouldEnableCountrySelectionField( + DerivCountrySelectionState countrySelectionState, + ) { + if (countrySelectionState is DerivCountrySelectionLoadedState || + countrySelectionState is DerivCountryChangedState || + countrySelectionState is DerivCountryConsentChangedState) { + return true; + } + + return false; + } + @override void dispose() { _focusNode.dispose(); diff --git a/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_residence_model.dart b/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_residence_model.dart index 2330eb971..c7f3744dd 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_residence_model.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_residence_model.dart @@ -7,6 +7,7 @@ class DerivResidenceModel extends Equatable { required this.isDisabled, required this.name, required this.code, + this.value, }); /// Disabled. @@ -18,6 +19,9 @@ class DerivResidenceModel extends Equatable { /// 2-letter country code. final String code; + /// 2-letter country code + final String? value; + @override List get props => [isDisabled, name, code]; } diff --git a/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_cubit_test.dart b/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_cubit_test.dart index 7d854c8bc..3a19c887c 100644 --- a/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_cubit_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_cubit_test.dart @@ -27,7 +27,7 @@ void main() { blocTest( 'emits DerivCountrySelectionLoadedState with filtered countries when fetchResidenceCounties is called', build: () => cubit, - act: (cubit) => cubit.fetchResidenceCounties(), + act: (cubit) => cubit.fetchResidenceCountries(), expect: () => [isA()], ); }); diff --git a/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_state_test.dart b/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_state_test.dart index 146c66014..0eca23677 100644 --- a/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_state_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_state_test.dart @@ -27,7 +27,7 @@ void main() { blocTest( 'emits DerivCountrySelectionLoadedState with filtered countries when fetchResidenceCounties is called', build: () => cubit, - act: (cubit) => cubit.fetchResidenceCounties(), + act: (cubit) => cubit.fetchResidenceCountries(), expect: () => [ const DerivCountrySelectionLoadedState([ DerivResidenceModel( diff --git a/packages/deriv_ui/lib/presentation/widgets/custom_checkbox.dart b/packages/deriv_ui/lib/presentation/widgets/custom_checkbox.dart index e47177718..31a6391d3 100644 --- a/packages/deriv_ui/lib/presentation/widgets/custom_checkbox.dart +++ b/packages/deriv_ui/lib/presentation/widgets/custom_checkbox.dart @@ -8,6 +8,8 @@ class CustomCheckbox extends StatelessWidget { required this.message, this.value = false, this.onValueChanged, + this.padding = const EdgeInsets.only(top: ThemeProvider.margin08), + this.contentsVerticalAlignment = CrossAxisAlignment.center, Key? key, }) : super(key: key); @@ -20,11 +22,24 @@ class CustomCheckbox extends StatelessWidget { /// A callback for the listeners of a checkbox, when the checkbox's value changes. final Function({bool? isChecked})? onValueChanged; + /// Padding of the checkbox. + final EdgeInsetsGeometry padding; + + /// The vertical alignment of the check and message within the row. + /// Defaults to [CrossAxisAlignment.center]. + /// + /// Example use case: + /// If the checkbox's message is longer than the width of the screen, + /// the message will be wrapped to the multiple line. Then, the message and + /// the checkbox can be aligned vertically top, center, or bottom of the row. + final CrossAxisAlignment contentsVerticalAlignment; + @override Widget build(BuildContext context) => Container( - padding: const EdgeInsets.only(top: ThemeProvider.margin08), + padding: padding, transform: Matrix4.translationValues(-14, 0, 0), child: Row( + crossAxisAlignment: contentsVerticalAlignment, children: [ Theme( data: ThemeData( From e5abb7d3a4ea44ca782cab73f559df3bb1e9282d Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:30:59 +0800 Subject: [PATCH 11/44] deps: remove relative path --- packages/deriv_auth_ui/example/pubspec.yaml | 10 +++++----- packages/deriv_auth_ui/pubspec.yaml | 12 ++++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/deriv_auth_ui/example/pubspec.yaml b/packages/deriv_auth_ui/example/pubspec.yaml index e8fb411c6..d495c6853 100644 --- a/packages/deriv_auth_ui/example/pubspec.yaml +++ b/packages/deriv_auth_ui/example/pubspec.yaml @@ -15,11 +15,11 @@ dependencies: deriv_auth_ui: path: ../ deriv_auth: - # git: - # url: git@github.com:regentmarkets/flutter-deriv-packages.git - # path: packages/deriv_auth - # ref: dev - path: ../../deriv_auth + git: + url: git@github.com:sahani-deriv/flutter-deriv-packages.git + path: packages/deriv_auth + ref: auth-ui-update + deriv_theme: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git diff --git a/packages/deriv_auth_ui/pubspec.yaml b/packages/deriv_auth_ui/pubspec.yaml index 96a7da6b3..240f1ad66 100644 --- a/packages/deriv_auth_ui/pubspec.yaml +++ b/packages/deriv_auth_ui/pubspec.yaml @@ -15,14 +15,22 @@ dependencies: flutter_bloc: ^8.1.3 deriv_auth: - path: ../deriv_auth + git: + url: git@github.com:sahani-deriv/flutter-deriv-packages.git + path: packages/deriv_auth + ref: auth-ui-update + deriv_theme: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme ref: dev deriv_ui: - path: ../deriv_ui + git: + url: git@github.com:sahani-deriv/flutter-deriv-packages.git + path: packages/deriv_ui + ref: auth-ui-update + flutter_svg: ^1.1.6 smooth_page_indicator: ^1.1.0 intl_utils: ^2.8.3 From 84e2099d65abe8585c64773254fca99059b3b4cb Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Mon, 30 Oct 2023 22:10:12 +0800 Subject: [PATCH 12/44] refactor: auth state handler --- .../example_auth_error_state_handler.dart | 16 +++ .../lib/features/login/pages/login_page.dart | 3 +- .../signup/pages/set_password_page.dart | 3 +- .../features/signup/pages/signup_page.dart | 3 +- .../core/states/auth_error_state_handler.dart | 122 ++++++++++++++---- .../core/states/auth_error_state_mapper.dart | 2 +- .../src/core/states/auth_state_listener.dart | 13 +- .../default_auth_error_state_handler.dart | 108 ---------------- .../lib/src/core/states/states.dart | 3 +- .../login/layouts/deriv_login_layout.dart | 12 +- .../layouts/deriv_set_password_layout.dart | 6 +- .../signup/layouts/deriv_signup_layout.dart | 9 +- .../layouts/deriv_login_layout_test.dart | 10 -- .../deriv_set_password_layout_test.dart | 5 - .../layouts/deriv_signup_layout_test.dart | 70 +++++----- packages/deriv_auth_ui/test/mocks.dart | 3 - 16 files changed, 169 insertions(+), 219 deletions(-) create mode 100644 packages/deriv_auth_ui/example/lib/core/example_auth_error_state_handler.dart delete mode 100644 packages/deriv_auth_ui/lib/src/core/states/default_auth_error_state_handler.dart diff --git a/packages/deriv_auth_ui/example/lib/core/example_auth_error_state_handler.dart b/packages/deriv_auth_ui/example/lib/core/example_auth_error_state_handler.dart new file mode 100644 index 000000000..478000d8e --- /dev/null +++ b/packages/deriv_auth_ui/example/lib/core/example_auth_error_state_handler.dart @@ -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), + ), + ); + } +} diff --git a/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart b/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart index e1f8e6168..2c6ad3518 100644 --- a/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart +++ b/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart @@ -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'; @@ -32,7 +33,7 @@ class _LoginPageState extends State { builder: (context) => const HomePage(), ), ), - authErrorStateHandler: DefaultAuthErrorStateHandler(context: context), + authErrorStateHandler: ExampleAuthErrorStateHandler(context: context), onLoginError: (_) {}, onResetPassTapped: () => Navigator.push( context, diff --git a/packages/deriv_auth_ui/example/lib/features/signup/pages/set_password_page.dart b/packages/deriv_auth_ui/example/lib/features/signup/pages/set_password_page.dart index 0dd3acec9..32b6b7cfa 100644 --- a/packages/deriv_auth_ui/example/lib/features/signup/pages/set_password_page.dart +++ b/packages/deriv_auth_ui/example/lib/features/signup/pages/set_password_page.dart @@ -1,5 +1,6 @@ import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_auth_ui/deriv_auth_ui.dart'; +import 'package:example/core/example_auth_error_state_handler.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -11,7 +12,7 @@ class SetPasswordPage extends StatelessWidget { @override Widget build(BuildContext context) { return DerivSetPasswordLayout( - authErrorStateHandler: DefaultAuthErrorStateHandler(context: context), + authErrorStateHandler: ExampleAuthErrorStateHandler(context: context), onDerivSignupState: (context, state) { if (state is DerivSignupDoneState) { context diff --git a/packages/deriv_auth_ui/example/lib/features/signup/pages/signup_page.dart b/packages/deriv_auth_ui/example/lib/features/signup/pages/signup_page.dart index f93304bfc..c275b0dc7 100644 --- a/packages/deriv_auth_ui/example/lib/features/signup/pages/signup_page.dart +++ b/packages/deriv_auth_ui/example/lib/features/signup/pages/signup_page.dart @@ -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'; @@ -13,7 +14,7 @@ class SignupPage extends StatefulWidget { class _SignupPageState extends State { @override Widget build(BuildContext context) => DerivSignupLayout( - authErrorStateHandler: DefaultAuthErrorStateHandler(context: context), + authErrorStateHandler: ExampleAuthErrorStateHandler(context: context), signupPageLabel: 'Start trading with Deriv', signupPageDescription: 'Join over 1 million traders worldwide who loves trading at Deriv.', diff --git a/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart index f589bb2f5..083148a34 100644 --- a/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart +++ b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart @@ -1,41 +1,109 @@ import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; +import 'package:deriv_ui/deriv_ui.dart'; +import 'package:flutter/material.dart'; -/// {@template auth_state_listener} -/// An abstract class that represents all the possible case of the -/// [DerivAuthErrorState]. Each method will be called when the corresponding -/// error state is emitted and can be implemented by the client app to handle -/// the error. This class is also to make sure that each error case is handled. +/// {@template default_auth_error_state_handler} +/// Base class for handling [DerivAuthErrorState]s. Client app can extend this +/// class and override the methods to handle the error states based on their +/// customization. /// {@endtemplate} -abstract class AuthErrorStateHandler { - /// {@macro auth_state_listener} +base class AuthErrorStateHandler { + /// {@macro default_auth_error_state_handler} + AuthErrorStateHandler({ + required this.context, + }); - /// User has set up 2FA and needs to enter 2FA. - void onMissingOtp(DerivAuthErrorState state); + /// The [BuildContext] of the widget that is using this handler. + final BuildContext context; - /// Account is self closed. - void onSelfClosed(DerivAuthErrorState state); + /// On invalid 2FA code. + void invalid2faCode(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informInvalid2FACode, + actionLabel: context.localization.actionTryAgain, + ); + } - /// Account is not supported in the country. - void onUnsupportedCountry(DerivAuthErrorState state); + /// On account is not activated. + void onAccountUnavailable(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informDeactivatedAccount, + actionLabel: context.localization.actionTryAgain, + ); + } - /// Invalid credentials. - void onInvalidCredentials(DerivAuthErrorState state); + /// On expired account. + void onExpiredAccount(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informExpiredAccount, + actionLabel: context.localization.actionTryAgain, + ); + } - /// Account is unavailable. - void onAccountUnavailable(DerivAuthErrorState state); + /// On failed authorization. + void onFailedAuthorization(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informFailedAuthorization, + actionLabel: context.localization.actionTryAgain, + ); + } - /// Invalid 2FA code. - void invalid2faCode(DerivAuthErrorState state); + /// User is trying to authenticate from an unsupported residence. + void onInavlidResidence(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informInvalidResidence, + actionLabel: context.localization.actionTryAgain, + ); + } - /// Failed authorization. - void onFailedAuthorization(DerivAuthErrorState state); + /// On invalid credentials. + void onInvalidCredentials(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informInvalidCredentials, + actionLabel: context.localization.actionTryAgain, + ); + } - /// User is trying to authenticate from an unsupported residence. - void onInavlidResidence(DerivAuthErrorState state); + /// User has set up 2FA and needs to enter 2FA. + void onMissingOtp(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informMissingOtp, + actionLabel: context.localization.actionTryAgain, + ); + } + + /// On self closed account. + void onSelfClosed(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informSelfClosed, + actionLabel: context.localization.actionTryAgain, + ); + } - /// User is trying to authenticate from an expired account. - void onExpiredAccount(DerivAuthErrorState state); + /// On unexpected error. + void onUnexpectedError(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informUnexpectedError, + actionLabel: context.localization.actionTryAgain, + ); + } - /// Default error handler for unexpected case. - void onUnexpectedError(DerivAuthErrorState state); + /// Account is not supported in the country. + void onUnsupportedCountry(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informUnsupportedCountry, + actionLabel: context.localization.actionTryAgain, + ); + } } diff --git a/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart index 4b8eb1a73..352188837 100644 --- a/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart +++ b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart @@ -1,5 +1,5 @@ import 'package:deriv_auth/deriv_auth.dart'; -import 'package:deriv_auth_ui/src/core/states/auth_error_state_handler.dart'; +import 'package:deriv_auth_ui/deriv_auth_ui.dart'; /// Maps the [DerivAuthErrorState] to the corresponding [AuthErrorStateHandler]. void authErrorStateMapper({ diff --git a/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart b/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart index 5a308065c..0e44d434e 100644 --- a/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart +++ b/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart @@ -34,7 +34,7 @@ class AuthStateListener extends StatelessWidget { /// Callback to be called when an error occurs. final Function(DerivAuthErrorState)? onError; - /// Implementation of [AuthErrorStateHandler]. + /// Extension of base [AuthErrorStateHandler]. If not provided, base implementation will be used. final AuthErrorStateHandler? authErrorStateHandler; @override @@ -50,12 +50,11 @@ class AuthStateListener extends StatelessWidget { } else if (state is DerivAuthErrorState) { onError?.call(state); - if (authErrorStateHandler != null) { - authErrorStateMapper( - authErrorState: state, - authErrorStateHandler: authErrorStateHandler!, - ); - } + authErrorStateMapper( + authErrorState: state, + authErrorStateHandler: authErrorStateHandler ?? + AuthErrorStateHandler(context: context), + ); } }, child: child, diff --git a/packages/deriv_auth_ui/lib/src/core/states/default_auth_error_state_handler.dart b/packages/deriv_auth_ui/lib/src/core/states/default_auth_error_state_handler.dart deleted file mode 100644 index 156a5877d..000000000 --- a/packages/deriv_auth_ui/lib/src/core/states/default_auth_error_state_handler.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:deriv_auth/deriv_auth.dart'; -import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; -import 'package:deriv_auth_ui/src/core/states/auth_error_state_handler.dart'; -import 'package:deriv_ui/deriv_ui.dart'; -import 'package:flutter/material.dart'; - -/// {@template default_auth_error_state_handler} -/// Default implementation of [AuthErrorStateHandler]. -/// {@endtemplate} -class DefaultAuthErrorStateHandler implements AuthErrorStateHandler { - /// {@macro default_auth_error_state_handler} - DefaultAuthErrorStateHandler({ - required this.context, - }); - - /// The [BuildContext] of the widget that is using this handler. - final BuildContext context; - - @override - void invalid2faCode(DerivAuthErrorState state) { - showErrorDialog( - context: context, - errorMessage: context.localization.informInvalid2FACode, - actionLabel: context.localization.actionTryAgain, - ); - } - - @override - void onAccountUnavailable(DerivAuthErrorState state) { - showErrorDialog( - context: context, - errorMessage: context.localization.informDeactivatedAccount, - actionLabel: context.localization.actionTryAgain, - ); - } - - @override - void onExpiredAccount(DerivAuthErrorState state) { - showErrorDialog( - context: context, - errorMessage: context.localization.informExpiredAccount, - actionLabel: context.localization.actionTryAgain, - ); - } - - @override - void onFailedAuthorization(DerivAuthErrorState state) { - showErrorDialog( - context: context, - errorMessage: context.localization.informFailedAuthorization, - actionLabel: context.localization.actionTryAgain, - ); - } - - @override - void onInavlidResidence(DerivAuthErrorState state) { - showErrorDialog( - context: context, - errorMessage: context.localization.informInvalidResidence, - actionLabel: context.localization.actionTryAgain, - ); - } - - @override - void onInvalidCredentials(DerivAuthErrorState state) { - showErrorDialog( - context: context, - errorMessage: context.localization.informInvalidCredentials, - actionLabel: context.localization.actionTryAgain, - ); - } - - @override - void onMissingOtp(DerivAuthErrorState state) { - showErrorDialog( - context: context, - errorMessage: context.localization.informMissingOtp, - actionLabel: context.localization.actionTryAgain, - ); - } - - @override - void onSelfClosed(DerivAuthErrorState state) { - showErrorDialog( - context: context, - errorMessage: context.localization.informSelfClosed, - actionLabel: context.localization.actionTryAgain, - ); - } - - @override - void onUnexpectedError(DerivAuthErrorState state) { - showErrorDialog( - context: context, - errorMessage: context.localization.informUnexpectedError, - actionLabel: context.localization.actionTryAgain, - ); - } - - @override - void onUnsupportedCountry(DerivAuthErrorState state) { - showErrorDialog( - context: context, - errorMessage: context.localization.informUnsupportedCountry, - actionLabel: context.localization.actionTryAgain, - ); - } -} diff --git a/packages/deriv_auth_ui/lib/src/core/states/states.dart b/packages/deriv_auth_ui/lib/src/core/states/states.dart index bab4ee7d2..7545c9d18 100644 --- a/packages/deriv_auth_ui/lib/src/core/states/states.dart +++ b/packages/deriv_auth_ui/lib/src/core/states/states.dart @@ -1,3 +1,2 @@ -export 'auth_error_state_handler.dart'; export 'auth_error_state_mapper.dart'; -export 'default_auth_error_state_handler.dart'; +export 'auth_error_state_handler.dart'; diff --git a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart index 976a754ec..30187be4a 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart @@ -1,10 +1,9 @@ import 'dart:async'; import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; -import 'package:deriv_auth_ui/src/core/states/auth_error_state_handler.dart'; -import 'package:deriv_auth_ui/src/core/states/auth_error_state_mapper.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_divider.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_panel.dart'; import 'package:deriv_theme/deriv_theme.dart'; @@ -22,7 +21,7 @@ class DerivLoginLayout extends StatefulWidget { required this.onSocialAuthButtonPressed, required this.welcomeLabel, required this.greetingLabel, - required this.authErrorStateHandler, + this.authErrorStateHandler, this.onLoginError, this.onLoginTapped, Key? key, @@ -34,8 +33,8 @@ class DerivLoginLayout extends StatefulWidget { /// Callback to be called when signup button is tapped. final VoidCallback onSignupTapped; - /// Implementation of [AuthErrorStateHandler]. - final AuthErrorStateHandler authErrorStateHandler; + /// Extension of base [AuthErrorStateHandler]. If not provided, base implementation will be used. + final AuthErrorStateHandler? authErrorStateHandler; /// Callback to be called when login error occurs. final Function(DerivAuthErrorState)? onLoginError; @@ -272,7 +271,8 @@ class _DerivLoginLayoutState extends State { authErrorStateMapper( authErrorState: state, - authErrorStateHandler: widget.authErrorStateHandler, + authErrorStateHandler: widget.authErrorStateHandler ?? + AuthErrorStateHandler(context: context), ); } diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart index 36d1d11f4..013bf03d7 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart @@ -19,7 +19,7 @@ class DerivSetPasswordLayout extends StatefulWidget { required this.onPreviousPressed, required this.verificationCode, required this.residence, - required this.authErrorStateHandler, + this.authErrorStateHandler, this.utmModel, this.onAuthError, Key? key, @@ -34,8 +34,8 @@ class DerivSetPasswordLayout extends StatefulWidget { /// Utm model final DerivAuthUtmModel? utmModel; - /// Implementation of [AuthErrorStateHandler]. - final AuthErrorStateHandler authErrorStateHandler; + /// Extension of base [AuthErrorStateHandler]. If not provided, base implementation will be used. + final AuthErrorStateHandler? authErrorStateHandler; /// Callback to be called on [DerivAuthErrorState] final Function(DerivAuthErrorState)? onAuthError; diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart index b09eeee4a..7bd2ffa7b 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart @@ -1,5 +1,4 @@ import 'package:deriv_auth/deriv_auth.dart'; -import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; import 'package:deriv_auth_ui/src/core/states/auth_state_listener.dart'; @@ -10,6 +9,8 @@ import 'package:deriv_ui/deriv_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:deriv_auth_ui/src/core/states/auth_error_state_handler.dart'; + /// It offers creating demo accounts via email and third-party providers. /// It Also provides optional referral code section which can be disabled /// by setting [enableReferralSection] to false. @@ -23,7 +24,7 @@ class DerivSignupLayout extends StatefulWidget { required this.onLoginTapped, required this.signupPageLabel, required this.signupPageDescription, - required this.authErrorStateHandler, + this.authErrorStateHandler, this.enableReferralSection = true, this.onAuthError, Key? key, @@ -35,8 +36,8 @@ class DerivSignupLayout extends StatefulWidget { /// Callback to be called when signup error occurs. final Function(DerivSignupErrorState) onSingupError; - /// Implementation of [AuthErrorStateHandler]. - final AuthErrorStateHandler authErrorStateHandler; + /// Extension of base [AuthErrorStateHandler]. If not provided, base implementation will be used. + final AuthErrorStateHandler? authErrorStateHandler; /// Callback to be called on [DerivAuthErrorState]. /// Useful if needed to do anything additional to [authErrorStateHandler]. diff --git a/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart b/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart index 1a97fe8d0..71971d510 100644 --- a/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart @@ -14,8 +14,6 @@ import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; -class MockAuthStateErrorHandler extends Mock implements AuthErrorStateHandler {} - void main() { group('DerivLoginLayout', () { late MockAuthCubit authCubit; @@ -46,7 +44,6 @@ void main() { onLoggedIn: (_) {}, onSocialAuthButtonPressed: (p0) {}, onLoginError: (_) {}, - authErrorStateHandler: MockAuthStateErrorHandler(), ), ), ); @@ -78,7 +75,6 @@ void main() { onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) {}, onLoginError: (_) {}, - authErrorStateHandler: MockAuthStateErrorHandler(), ), ), ); @@ -112,7 +108,6 @@ void main() { onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) {}, onLoginError: (_) {}, - authErrorStateHandler: MockAuthStateErrorHandler(), ), )); @@ -142,7 +137,6 @@ void main() { onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) {}, onLoginError: (_) {}, - authErrorStateHandler: MockAuthStateErrorHandler(), ), )); @@ -181,7 +175,6 @@ void main() { }, onSocialAuthButtonPressed: (_) {}, onLoginError: (_) {}, - authErrorStateHandler: MockAuthStateErrorHandler(), ), )); @@ -215,7 +208,6 @@ void main() { }, onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) {}, - authErrorStateHandler: MockAuthStateErrorHandler(), ), )); @@ -245,7 +237,6 @@ void main() { onLoggedIn: (_) {}, onSocialAuthButtonPressed: (_) {}, onLoginError: (_) {}, - authErrorStateHandler: MockAuthStateErrorHandler(), ), )); @@ -278,7 +269,6 @@ void main() { onSocialAuthButtonPressedCalled = true; }, onLoginError: (_) {}, - authErrorStateHandler: MockAuthStateErrorHandler(), ), )); diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart index 45a2a588e..6928e9fb7 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart @@ -40,7 +40,6 @@ void main() { BlocProvider.value(value: signupCubit), ], child: DerivSetPasswordLayout( - authErrorStateHandler: MockAuthErrorStateHandler(), onDerivSignupState: (_, __) {}, onPreviousPressed: () {}, verificationCode: '123456', @@ -71,7 +70,6 @@ void main() { BlocProvider.value(value: signupCubit), ], child: DerivSetPasswordLayout( - authErrorStateHandler: MockAuthErrorStateHandler(), onDerivSignupState: (_, __) => isOnDerivSignupStateCalled = true, onPreviousPressed: () {}, @@ -94,7 +92,6 @@ void main() { BlocProvider.value(value: signupCubit), ], child: DerivSetPasswordLayout( - authErrorStateHandler: MockAuthErrorStateHandler(), onDerivSignupState: (_, __) {}, onPreviousPressed: () => isOnPreviousPressedCalled = true, verificationCode: 'verificationCode', @@ -117,7 +114,6 @@ void main() { BlocProvider.value(value: signupCubit), ], child: DerivSetPasswordLayout( - authErrorStateHandler: MockAuthErrorStateHandler(), onDerivSignupState: (_, __) {}, onPreviousPressed: () {}, verificationCode: 'verificationCode', @@ -147,7 +143,6 @@ void main() { BlocProvider.value(value: signupCubit), ], child: DerivSetPasswordLayout( - authErrorStateHandler: MockAuthErrorStateHandler(), onDerivSignupState: (_, __) {}, onPreviousPressed: () {}, verificationCode: 'verificationCode', diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart index 321b534f3..679da1ad1 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart @@ -52,7 +52,6 @@ void main() { onSingupEmailSent: (_) {}, onSignupPressed: () {}, onLoginTapped: () {}, - authErrorStateHandler: MockAuthErrorStateHandler(), ), ), )); @@ -82,7 +81,6 @@ void main() { onSingupEmailSent: (_) {}, onSignupPressed: () {}, onLoginTapped: () {}, - authErrorStateHandler: MockAuthErrorStateHandler(), ), ), )); @@ -107,15 +105,13 @@ void main() { child: BlocProvider.value( value: signupCubit, child: DerivSignupLayout( - signupPageLabel: signupPageLabel, - signupPageDescription: signupPageDescription, - onSocialAuthButtonPressed: (_) {}, - onSingupError: (_) {}, - onSingupEmailSent: (_) => isOnSignupEmailSentCalled = true, - onSignupPressed: () {}, - onLoginTapped: () {}, - authErrorStateHandler: MockAuthErrorStateHandler(), - ), + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) {}, + onSingupEmailSent: (_) => isOnSignupEmailSentCalled = true, + onSignupPressed: () {}, + onLoginTapped: () {}), ), )); @@ -134,15 +130,13 @@ void main() { child: BlocProvider.value( value: signupCubit, child: DerivSignupLayout( - signupPageLabel: signupPageLabel, - signupPageDescription: signupPageDescription, - onSocialAuthButtonPressed: (_) {}, - onSingupError: (_) {}, - onSingupEmailSent: (_) {}, - onSignupPressed: () => isOnSignupPressedCalled = true, - onLoginTapped: () {}, - authErrorStateHandler: MockAuthErrorStateHandler(), - ), + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) {}, + onSingupEmailSent: (_) {}, + onSignupPressed: () => isOnSignupPressedCalled = true, + onLoginTapped: () {}), ), )); @@ -166,17 +160,15 @@ void main() { child: BlocProvider.value( value: signupCubit, child: DerivSignupLayout( - signupPageLabel: signupPageLabel, - signupPageDescription: signupPageDescription, - onSocialAuthButtonPressed: (_) {}, - onSingupError: (_) {}, - onSingupEmailSent: (_) {}, - onSignupPressed: () {}, - onLoginTapped: () { - isOnLoginTappedCalled = true; - }, - authErrorStateHandler: MockAuthErrorStateHandler(), - ), + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) {}, + onSingupEmailSent: (_) {}, + onSignupPressed: () {}, + onLoginTapped: () { + isOnLoginTappedCalled = true; + }), ), )); @@ -203,15 +195,13 @@ void main() { child: BlocProvider.value( value: signupCubit, child: DerivSignupLayout( - signupPageLabel: signupPageLabel, - signupPageDescription: signupPageDescription, - onSocialAuthButtonPressed: (_) {}, - onSingupError: (_) => isOnSignupErrorCalled = true, - onSingupEmailSent: (_) {}, - onSignupPressed: () {}, - onLoginTapped: () {}, - authErrorStateHandler: MockAuthErrorStateHandler(), - ), + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) => isOnSignupErrorCalled = true, + onSingupEmailSent: (_) {}, + onSignupPressed: () {}, + onLoginTapped: () {}), ), )); diff --git a/packages/deriv_auth_ui/test/mocks.dart b/packages/deriv_auth_ui/test/mocks.dart index 3429c7e51..ca42e2da0 100644 --- a/packages/deriv_auth_ui/test/mocks.dart +++ b/packages/deriv_auth_ui/test/mocks.dart @@ -1,5 +1,4 @@ import 'package:deriv_auth/deriv_auth.dart'; -import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:mocktail/mocktail.dart'; class MockAuthCubit extends Mock implements DerivAuthCubit {} @@ -7,5 +6,3 @@ class MockAuthCubit extends Mock implements DerivAuthCubit {} class MockDerivResetPassCubit extends Mock implements DerivResetPassCubit {} class MockSignupCubit extends Mock implements DerivSignupCubit {} - -class MockAuthErrorStateHandler extends Mock implements AuthErrorStateHandler {} From aa5fd054884e4ca91ce6e45be2a734a4798b5cb3 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Mon, 30 Oct 2023 23:39:40 +0800 Subject: [PATCH 13/44] test: country selection cubit test --- .../cubits/deriv_country_selection_cubit.dart | 4 +- .../deriv_country_selection_layout.dart | 2 +- .../signup/models/deriv_residence_model.dart | 4 - .../deriv_country_selection_cubit_test.dart | 88 ++++++++++++++++--- .../deriv_country_selection_state_test.dart | 39 -------- 5 files changed, 80 insertions(+), 57 deletions(-) delete mode 100644 packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_state_test.dart diff --git a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart index 355db8bd5..9e0c29751 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart @@ -20,7 +20,7 @@ class DerivCountrySelectionCubit extends Cubit { final List filteredCountries = countries .where( (DerivResidenceModel country) => - isAllowedCountry(countryCode: country.value), + isAllowedCountry(countryCode: country.code), ) .toList(); @@ -42,7 +42,7 @@ class DerivCountrySelectionCubit extends Cubit { state.countries, selectedCountry: selectedCountry, selectedCountryRequiresConsent: isConsentRequired( - countryCode: selectedCountry.value, + countryCode: selectedCountry.code, ), ), ); diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart index 2c931576c..ee5c591c5 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart @@ -214,7 +214,7 @@ class _DerivCountrySelectionLayoutState message: countryConsentMessage ?? getCountryConsentMessage( context, - countryCode: selectedCountry.value, + countryCode: selectedCountry.code, ), ); }, diff --git a/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_residence_model.dart b/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_residence_model.dart index c7f3744dd..2330eb971 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_residence_model.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_residence_model.dart @@ -7,7 +7,6 @@ class DerivResidenceModel extends Equatable { required this.isDisabled, required this.name, required this.code, - this.value, }); /// Disabled. @@ -19,9 +18,6 @@ class DerivResidenceModel extends Equatable { /// 2-letter country code. final String code; - /// 2-letter country code - final String? value; - @override List get props => [isDisabled, name, code]; } diff --git a/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_cubit_test.dart b/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_cubit_test.dart index 3a19c887c..ea764dc15 100644 --- a/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_cubit_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_cubit_test.dart @@ -6,29 +6,95 @@ import 'package:flutter_test/flutter_test.dart'; void main() { group('DerivCountrySelectionCubit', () { - late DerivCountrySelectionCubit cubit; + late DerivCountrySelectionCubit countrySelectionCubit; + late List countriesList; setUp(() { - final countries = [ + countriesList = [ const DerivResidenceModel( code: 'us', name: 'United States', isDisabled: false), + const DerivResidenceModel( + code: 'br', name: 'Brazil', isDisabled: false), const DerivResidenceModel( code: 'fr', name: 'France', isDisabled: false), const DerivResidenceModel( code: 'de', name: 'Germany', isDisabled: false), ]; - cubit = DerivCountrySelectionCubit(Future.value(countries)); - }); - test('initial state is DerivCountrySelectionInitialState', () { - expect(cubit.state, const DerivCountrySelectionInitialState()); + countrySelectionCubit = + DerivCountrySelectionCubit(Future.value(countriesList)); }); blocTest( - 'emits DerivCountrySelectionLoadedState with filtered countries when fetchResidenceCounties is called', - build: () => cubit, - act: (cubit) => cubit.fetchResidenceCountries(), - expect: () => [isA()], - ); + 'Initial states is [DerivCountrySelectionInitialState]', + build: () => countrySelectionCubit, + verify: (DerivCountrySelectionCubit cubit) { + expect( + cubit.state, + isA(), + ); + }); + + blocTest( + 'Verify cubit emits the right states by loading country list', + build: () => countrySelectionCubit, + act: (DerivCountrySelectionCubit cubit) => + countrySelectionCubit.fetchResidenceCountries(), + verify: (DerivCountrySelectionCubit cubit) { + expect( + cubit.state, + isA(), + ); + }); + + blocTest( + 'Verify cubit emits the right states by changing the country selection.', + build: () => countrySelectionCubit, + seed: () => DerivCountrySelectionLoadedState(countriesList), + act: (DerivCountrySelectionCubit cubit) => cubit.changeSelectedCountry( + selectedCountry: countriesList[0], + ), + verify: (DerivCountrySelectionCubit cubit) { + expect( + countrySelectionCubit.state, + isA(), + ); + + expect( + countrySelectionCubit.state.selectedCountry?.code, + countriesList[0].code, + ); + }); + + blocTest( + 'Verify cubit emits the right states when it changing the consent status(Checkbox).', + build: () => countrySelectionCubit, + seed: () => DerivCountrySelectionLoadedState(countriesList), + act: (DerivCountrySelectionCubit cubit) { + cubit + ..changeSelectedCountry( + selectedCountry: const DerivResidenceModel( + code: 'br', name: 'Brazil', isDisabled: false), + ) + ..updateCountryConsentStatus( + agreedToTerms: !cubit.state.agreedToTerms, + ); + }, + verify: (DerivCountrySelectionCubit cubit) { + expect( + cubit.state, + isA(), + ); + + expect( + cubit.state.selectedCountryRequiresConsent, + true, + ); + + expect( + cubit.state.agreedToTerms, + true, + ); + }); }); } diff --git a/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_state_test.dart b/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_state_test.dart deleted file mode 100644 index 0eca23677..000000000 --- a/packages/deriv_auth_ui/test/features/signup/cubits/deriv_country_selection_state_test.dart +++ /dev/null @@ -1,39 +0,0 @@ -// ignore_for_file: always_specify_types - -import 'package:bloc_test/bloc_test.dart'; -import 'package:deriv_auth_ui/deriv_auth_ui.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - group('DerivCountrySelectionCubit', () { - late DerivCountrySelectionCubit cubit; - - setUp(() { - final countries = [ - const DerivResidenceModel( - code: 'us', name: 'United States', isDisabled: false), - const DerivResidenceModel( - code: 'fr', name: 'France', isDisabled: false), - const DerivResidenceModel( - code: 'de', name: 'Germany', isDisabled: false), - ]; - cubit = DerivCountrySelectionCubit(Future.value(countries)); - }); - - test('initial state is DerivCountrySelectionInitialState', () { - expect(cubit.state, const DerivCountrySelectionInitialState()); - }); - - blocTest( - 'emits DerivCountrySelectionLoadedState with filtered countries when fetchResidenceCounties is called', - build: () => cubit, - act: (cubit) => cubit.fetchResidenceCountries(), - expect: () => [ - const DerivCountrySelectionLoadedState([ - DerivResidenceModel( - code: 'us', name: 'United States', isDisabled: false), - ]), - ], - ); - }); -} From 1b2940947ef58c1df5658ddfdff25bddc5fc523c Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:22:51 +0800 Subject: [PATCH 14/44] test: auth state handler --- .../lib/generated/intl/messages_en.dart | 4 + .../deriv_auth_ui/lib/generated/l10n.dart | 20 ++ packages/deriv_auth_ui/lib/l10n/intl_en.arb | 4 +- .../core/states/auth_error_state_handler.dart | 18 ++ .../core/states/auth_error_state_mapper.dart | 6 + .../states/auth_error_state_mapper_test.dart | 206 ++++++++++++++++++ .../core/states/auth_state_listener_test.dart | 134 ++++++++++++ .../layouts/deriv_login_layout_test.dart | 29 +++ .../deriv_set_password_layout_test.dart | 59 +++++ .../layouts/deriv_signup_layout_test.dart | 63 ++++++ 10 files changed, 542 insertions(+), 1 deletion(-) create mode 100644 packages/deriv_auth_ui/test/core/states/auth_error_state_mapper_test.dart create mode 100644 packages/deriv_auth_ui/test/core/states/auth_state_listener_test.dart diff --git a/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart b/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart index 898c7978e..258c5c064 100644 --- a/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart +++ b/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart @@ -54,6 +54,8 @@ class MessageLookup extends MessageLookupByLibrary { "actionTryAgain": MessageLookupByLibrary.simpleMessage("Try Again"), "infoReferralInfoDescription": MessageLookupByLibrary.simpleMessage( "An alphanumeric code provided by a Deriv affiliate, applicable for email sign-ups only."), + "informConnectionError": MessageLookupByLibrary.simpleMessage( + "Connection error. Please try again later."), "informDeactivatedAccount": MessageLookupByLibrary.simpleMessage( "Your account is deactivated."), "informEnterTwoFactorAuthCode": MessageLookupByLibrary.simpleMessage( @@ -98,6 +100,8 @@ class MessageLookup extends MessageLookupByLibrary { "informSelfClosed": MessageLookupByLibrary.simpleMessage( "Your account has been closed."), "informSendResetPasswordEmail": m0, + "informSwitchAccountError": MessageLookupByLibrary.simpleMessage( + "Switch account error. Please try again later."), "informUnexpectedError": MessageLookupByLibrary.simpleMessage( "An unexpected error occurred."), "informUnsupportedCountry": MessageLookupByLibrary.simpleMessage( diff --git a/packages/deriv_auth_ui/lib/generated/l10n.dart b/packages/deriv_auth_ui/lib/generated/l10n.dart index d71b0d79d..10f1c1aaf 100644 --- a/packages/deriv_auth_ui/lib/generated/l10n.dart +++ b/packages/deriv_auth_ui/lib/generated/l10n.dart @@ -820,6 +820,26 @@ class DerivAuthUILocalization { args: [], ); } + + /// `Connection error. Please try again later.` + String get informConnectionError { + return Intl.message( + 'Connection error. Please try again later.', + name: 'informConnectionError', + desc: '', + args: [], + ); + } + + /// `Switch account error. Please try again later.` + String get informSwitchAccountError { + return Intl.message( + 'Switch account error. Please try again later.', + name: 'informSwitchAccountError', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate diff --git a/packages/deriv_auth_ui/lib/l10n/intl_en.arb b/packages/deriv_auth_ui/lib/l10n/intl_en.arb index ce4590c35..a08cd5a19 100644 --- a/packages/deriv_auth_ui/lib/l10n/intl_en.arb +++ b/packages/deriv_auth_ui/lib/l10n/intl_en.arb @@ -102,5 +102,7 @@ "informUnexpectedError": "An unexpected error occurred.", "informUnsupportedCountry": "Your country is not supported.", "informExpiredAccount": "Your account is expired", - "labelCountryConsentBrazil": "I hereby confirm that my request for opening an account with Deriv to trade OTC products issued and offered exclusively outside Brazil was initiated by me. I fully understand that Deriv is not regulated by CVM and by approaching Deriv I intend to set up a relation with a foreign company." + "labelCountryConsentBrazil": "I hereby confirm that my request for opening an account with Deriv to trade OTC products issued and offered exclusively outside Brazil was initiated by me. I fully understand that Deriv is not regulated by CVM and by approaching Deriv I intend to set up a relation with a foreign company.", + "informConnectionError": "Connection error. Please try again later.", + "informSwitchAccountError": "Switch account error. Please try again later." } \ No newline at end of file diff --git a/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart index 083148a34..f7daa9184 100644 --- a/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart +++ b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_handler.dart @@ -106,4 +106,22 @@ base class AuthErrorStateHandler { actionLabel: context.localization.actionTryAgain, ); } + + /// On connection error. + void onConnectionError(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informConnectionError, + actionLabel: context.localization.actionTryAgain, + ); + } + + /// On switch account error. + void onSwitchAccountError(DerivAuthErrorState state) { + showErrorDialog( + context: context, + errorMessage: context.localization.informSwitchAccountError, + actionLabel: context.localization.actionTryAgain, + ); + } } diff --git a/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart index 352188837..ba6fb739e 100644 --- a/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart +++ b/packages/deriv_auth_ui/lib/src/core/states/auth_error_state_mapper.dart @@ -38,6 +38,12 @@ void authErrorStateMapper({ case AuthErrorType.expiredAccount: authErrorStateHandler.onExpiredAccount(authErrorState); return; + case AuthErrorType.connectionError: + authErrorStateHandler.onConnectionError(authErrorState); + return; + case AuthErrorType.switchAccountError: + authErrorStateHandler.onSwitchAccountError(authErrorState); + return; default: authErrorStateHandler.onUnexpectedError(authErrorState); return; diff --git a/packages/deriv_auth_ui/test/core/states/auth_error_state_mapper_test.dart b/packages/deriv_auth_ui/test/core/states/auth_error_state_mapper_test.dart new file mode 100644 index 000000000..12d6a5f74 --- /dev/null +++ b/packages/deriv_auth_ui/test/core/states/auth_error_state_mapper_test.dart @@ -0,0 +1,206 @@ +import 'package:flutter/material.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:test/test.dart'; +import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth_ui/deriv_auth_ui.dart'; + +class MockBuildContext extends Mock implements BuildContext {} + +// Create a mock implementation of AuthErrorStateHandler for testing. +final class MockAuthErrorStateHandler extends AuthErrorStateHandler { + MockAuthErrorStateHandler({required super.context}); + + DerivAuthErrorState? lastHandledError; + + @override + void onMissingOtp(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } + + @override + void onSelfClosed(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } + + @override + void onUnsupportedCountry(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } + + @override + void onAccountUnavailable(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } + + @override + void onInvalidCredentials(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } + + @override + void onFailedAuthorization(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } + + @override + void onInavlidResidence(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } + + @override + void onExpiredAccount(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } + + @override + void invalid2faCode(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } + + @override + void onConnectionError(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } + + @override + void onSwitchAccountError(DerivAuthErrorState errorState) { + lastHandledError = errorState; + } +} + +void main() { + // Create an instance of MockAuthErrorStateHandler for testing. + final MockAuthErrorStateHandler mockHandler = MockAuthErrorStateHandler( + context: MockBuildContext(), + ); + + test('authErrorStateMapper handles missing OTP', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.missingOtp, isSocialLogin: false, message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + + expect(mockHandler.lastHandledError, equals(errorState)); + }); + + test('authErrorStateMapper handles self closed', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.selfClosed, isSocialLogin: false, message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + + expect(mockHandler.lastHandledError, equals(errorState)); + }); + + test('authErrorStateMapper handles unsupported country', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.unsupportedCountry, + isSocialLogin: false, + message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + + expect(mockHandler.lastHandledError, equals(errorState)); + }); + + test('authErrorStateMapper handles account unavailable', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.accountUnavailable, + isSocialLogin: false, + message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + + expect(mockHandler.lastHandledError, equals(errorState)); + }); + + test('authErrorStateMapper handles invalid credentials', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.invalidCredential, + isSocialLogin: false, + message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + + expect(mockHandler.lastHandledError, equals(errorState)); + }); + + test('authErrorStateMapper handles failed authorization', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.failedAuthorization, + isSocialLogin: false, + message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + + expect(mockHandler.lastHandledError, equals(errorState)); + }); + + test('authErrorStateMapper handles invalid residence', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.invalidResidence, + isSocialLogin: false, + message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + + expect(mockHandler.lastHandledError, equals(errorState)); + }); + + test('authErrorStateMapper handles expired account', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.expiredAccount, isSocialLogin: false, message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + + expect(mockHandler.lastHandledError, equals(errorState)); + }); + + test('authErrorStateMapper handles invalid 2FA code', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.invalid2faCode, isSocialLogin: false, message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + expect(mockHandler.lastHandledError, equals(errorState)); + }); + + test('authErrorStateMapper handles connection error', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.connectionError, isSocialLogin: false, message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + expect(mockHandler.lastHandledError, equals(errorState)); + }); + + test('authErrorStateMapper handles switch account error', () { + final DerivAuthErrorState errorState = DerivAuthErrorState( + type: AuthErrorType.switchAccountError, + isSocialLogin: false, + message: ''); + authErrorStateMapper( + authErrorState: errorState, + authErrorStateHandler: mockHandler, + ); + expect(mockHandler.lastHandledError, equals(errorState)); + }); +} diff --git a/packages/deriv_auth_ui/test/core/states/auth_state_listener_test.dart b/packages/deriv_auth_ui/test/core/states/auth_state_listener_test.dart new file mode 100644 index 000000000..bfcfdb067 --- /dev/null +++ b/packages/deriv_auth_ui/test/core/states/auth_state_listener_test.dart @@ -0,0 +1,134 @@ +import 'package:deriv_auth/core/models/landig_comany_model.dart'; +import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth_ui/src/core/states/auth_state_listener.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:patrol_finders/patrol_finders.dart'; + +import '../../pump_app.dart'; + +class MockDerivAuthCubit extends Mock implements DerivAuthCubit {} + +class MockBuildContext extends Mock implements BuildContext {} + +void main() { + late DerivAuthCubit authCubit; + + setUpAll(() { + authCubit = MockDerivAuthCubit(); + + when(() => authCubit.close()).thenAnswer((_) async {}); + }); + + group('AuthStateLister', () { + patrolWidgetTest('onLoggedIn is called based on logged in state', + (PatrolTester $) async { + bool isOnLoggedInCalled = false; + + final DerivAuthLoggedInState mockAuthState = + DerivAuthLoggedInState(const DerivAuthModel( + authorizeEntity: AuthorizeEntity(), + landingCompany: LandingCompanyEntity(), + )); + + when(() => authCubit.state).thenAnswer((_) => mockAuthState); + + when(() => authCubit.stream).thenAnswer((_) => + Stream.fromIterable([mockAuthState])); + + await $.pumpApp( + BlocProvider( + create: (_) => authCubit, + child: AuthStateListener( + onLoggedIn: (_) { + isOnLoggedInCalled = true; + }, + child: Container()), + ), + ); + + expect(isOnLoggedInCalled, true); + }); + + patrolWidgetTest('onLoggedOut is called based on logged out state', + (PatrolTester $) async { + bool isOnLoggedOutCalled = false; + + final DerivAuthLoggedOutState mockAuthState = DerivAuthLoggedOutState(); + + when(() => authCubit.state).thenAnswer((_) => mockAuthState); + + when(() => authCubit.stream).thenAnswer((_) => + Stream.fromIterable([mockAuthState])); + + await $.pumpApp( + BlocProvider( + create: (_) => authCubit, + child: AuthStateListener( + onLoggedOut: () { + isOnLoggedOutCalled = true; + }, + child: Container()), + ), + ); + + expect(isOnLoggedOutCalled, true); + }); + + patrolWidgetTest('onLoading is called based on loading state', + (PatrolTester $) async { + bool isOnLoadingCalled = false; + + final DerivAuthLoadingState mockAuthState = DerivAuthLoadingState(); + + when(() => authCubit.state).thenAnswer((_) => mockAuthState); + + when(() => authCubit.stream).thenAnswer((_) => + Stream.fromIterable([mockAuthState])); + + await $.pumpApp( + BlocProvider( + create: (_) => authCubit, + child: AuthStateListener( + onLoading: () { + isOnLoadingCalled = true; + }, + child: Container()), + ), + ); + + expect(isOnLoadingCalled, true); + }); + + patrolWidgetTest('onError is called based on error state', + (PatrolTester $) async { + bool isOnErrorCalled = false; + + final DerivAuthErrorState mockAuthState = DerivAuthErrorState( + type: AuthErrorType.accountUnavailable, + message: 'error', + isSocialLogin: false, + ); + + when(() => authCubit.state).thenAnswer((_) => mockAuthState); + + when(() => authCubit.stream).thenAnswer((_) => + Stream.fromIterable([mockAuthState])); + + await $.pumpApp( + BlocProvider( + create: (_) => authCubit, + child: AuthStateListener( + onError: (_) { + isOnErrorCalled = true; + }, + child: Container()), + ), + ); + + expect(isOnErrorCalled, true); + }); + }); +} diff --git a/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart b/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart index 71971d510..f1256d055 100644 --- a/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart @@ -214,6 +214,35 @@ void main() { expect(onLoginErrorCalled, isTrue); }); + patrolWidgetTest('calls [AuthErrorStateHandler] on auth error state.', + (PatrolTester $) async { + final mockAuthState = DerivAuthErrorState( + isSocialLogin: false, + message: 'error', + type: AuthErrorType.failedAuthorization, + ); + + when(() => authCubit.state).thenAnswer((_) => mockAuthState); + + when(() => authCubit.stream) + .thenAnswer((_) => Stream.fromIterable([mockAuthState])); + + await $.pumpApp(BlocProvider.value( + value: authCubit, + child: DerivLoginLayout( + welcomeLabel: welcomeLabel, + greetingLabel: greetingLabel, + onResetPassTapped: () {}, + onSignupTapped: () {}, + onLoginError: (_) {}, + onLoggedIn: (_) {}, + onSocialAuthButtonPressed: (_) {}, + ), + )); + + expect($(PopupAlertDialog).$('Authorization failed.'), findsOneWidget); + }); + patrolWidgetTest('calls resetPassTapped when reset button is pressed.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart index 6928e9fb7..11426cab7 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart @@ -80,6 +80,65 @@ void main() { expect(isOnDerivSignupStateCalled, true); }); + patrolWidgetTest('calls [AuthErrorStateHandler] on auth error state.', + (PatrolTester $) async { + final mockAuthState = DerivAuthErrorState( + isSocialLogin: false, + message: 'error', + type: AuthErrorType.failedAuthorization, + ); + + when(() => authCubit.state).thenReturn(mockAuthState); + when(() => authCubit.stream) + .thenAnswer((_) => Stream.fromIterable([mockAuthState])); + + await $.pumpApp(MultiBlocProvider( + providers: [ + BlocProvider.value(value: authCubit), + BlocProvider.value(value: signupCubit), + ], + child: DerivSetPasswordLayout( + onDerivSignupState: (_, __) {}, + onPreviousPressed: () {}, + verificationCode: 'verificationCode', + residence: 'residence'), + )); + + expect($(PopupAlertDialog).$('Authorization failed.'), findsOneWidget); + }); + + patrolWidgetTest('onAuthError is called on auth error state.', + (PatrolTester $) async { + final mockAuthState = DerivAuthErrorState( + isSocialLogin: false, + message: 'error', + type: AuthErrorType.failedAuthorization, + ); + + when(() => authCubit.state).thenReturn(mockAuthState); + when(() => authCubit.stream) + .thenAnswer((_) => Stream.fromIterable([mockAuthState])); + + bool isOnAuthErrorCalled = false; + + await $.pumpApp(MultiBlocProvider( + providers: [ + BlocProvider.value(value: authCubit), + BlocProvider.value(value: signupCubit), + ], + child: DerivSetPasswordLayout( + onDerivSignupState: (_, __) {}, + onPreviousPressed: () {}, + onAuthError: (_) { + isOnAuthErrorCalled = true; + }, + verificationCode: 'verificationCode', + residence: 'residence'), + )); + + expect(isOnAuthErrorCalled, true); + }); + patrolWidgetTest('onPreviousPressed is called upon tapping previous button', (PatrolTester $) async { bool isOnPreviousPressedCalled = false; diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart index 679da1ad1..03e3010fd 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart @@ -207,5 +207,68 @@ void main() { expect(isOnSignupErrorCalled, true); }); + patrolWidgetTest('onAuthError is called upon auth error state', + (PatrolTester $) async { + bool isOnAuthErrorCalled = false; + + final mockAuthState = DerivAuthErrorState( + isSocialLogin: false, + message: 'error', + type: AuthErrorType.failedAuthorization, + ); + + when(() => authCubit.state).thenAnswer((_) => mockAuthState); + + when(() => authCubit.stream) + .thenAnswer((_) => Stream.fromIterable([mockAuthState])); + + await $.pumpApp(BlocProvider.value( + value: authCubit, + child: BlocProvider.value( + value: signupCubit, + child: DerivSignupLayout( + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) {}, + onSingupEmailSent: (_) {}, + onSignupPressed: () {}, + onAuthError: (_) => isOnAuthErrorCalled = true, + onLoginTapped: () {}), + ), + )); + + expect(isOnAuthErrorCalled, true); + }); + + patrolWidgetTest('calls [AuthErrorStateHandler] on auth error state.', + (PatrolTester $) async { + final mockAuthState = DerivAuthErrorState( + isSocialLogin: false, + message: 'error', + type: AuthErrorType.failedAuthorization, + ); + + when(() => authCubit.state).thenAnswer((_) => mockAuthState); + + when(() => authCubit.stream) + .thenAnswer((_) => Stream.fromIterable([mockAuthState])); + + await $.pumpApp(BlocProvider.value( + value: authCubit, + child: BlocProvider.value( + value: signupCubit, + child: DerivSignupLayout( + signupPageLabel: signupPageLabel, + signupPageDescription: signupPageDescription, + onSocialAuthButtonPressed: (_) {}, + onSingupError: (_) {}, + onSingupEmailSent: (_) {}, + onSignupPressed: () {}, + onLoginTapped: () {}), + ))); + + expect($(PopupAlertDialog).$('Authorization failed.'), findsOneWidget); + }); }); } From 185928c6d8489d8edf3732a58cf05d91b0cac71b Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:34:04 +0800 Subject: [PATCH 15/44] feat: add gesture detector in get started layout --- .../lib/features/get_started/pages/get_started_page.dart | 1 + .../get_started/layouts/deriv_get_started_layout.dart | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/deriv_auth_ui/example/lib/features/get_started/pages/get_started_page.dart b/packages/deriv_auth_ui/example/lib/features/get_started/pages/get_started_page.dart index d4ea64fe0..a89b5e67f 100644 --- a/packages/deriv_auth_ui/example/lib/features/get_started/pages/get_started_page.dart +++ b/packages/deriv_auth_ui/example/lib/features/get_started/pages/get_started_page.dart @@ -30,5 +30,6 @@ class GetStartedPage extends StatelessWidget { builder: (context) => const SignupPage(), ), ), + onTapNavigation: () {}, ); } diff --git a/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart b/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart index 160d26910..dbca53412 100644 --- a/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart @@ -20,6 +20,7 @@ class DerivGetStartedLayout extends StatefulWidget { required this.backgroundImagePath, required this.onLoginTapped, required this.onSignupTapped, + required this.onTapNavigation, Key? key, }) : super(key: key); @@ -38,6 +39,9 @@ class DerivGetStartedLayout extends StatefulWidget { /// Callback to be called when signup button is tapped. final VoidCallback onSignupTapped; + /// Navigation to be called when screen is tapped seven times. + final VoidCallback onTapNavigation; + @override State createState() => _DerivGetStartedLayoutState(); } @@ -102,7 +106,9 @@ class _DerivGetStartedLayoutState extends State { PreferredSizeWidget _buildAppBar(BuildContext context) => AppBar( backgroundColor: context.theme.colors.secondary, centerTitle: false, - title: SvgPicture.asset(widget.appLogoIconPath), + title: AppSettingGestureDetector( + onTapNavigation: widget.onTapNavigation, + child: SvgPicture.asset(widget.appLogoIconPath)), ); Timer _buildNewScrollTimer() => Timer.periodic( From afb1989104a02fc3e8c03a55e1b35216500be422 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Tue, 31 Oct 2023 14:14:04 +0800 Subject: [PATCH 16/44] feat: reset pass success page --- .../pages/choose_new_password_page.dart | 7 +-- .../pages/reset_pass_success_page.dart | 51 ++++++++++++++++ packages/deriv_auth_ui/lib/deriv_auth_ui.dart | 1 + .../layouts/deriv_choose_new_pass_layout.dart | 59 +------------------ .../deriv_success_pass_change_layout.dart | 59 +++++++++++++++++++ .../deriv_get_started_layout_test.dart | 3 + ...deriv_success_pass_change_layout_test.dart | 17 ++++++ 7 files changed, 136 insertions(+), 61 deletions(-) create mode 100644 packages/deriv_auth_ui/example/lib/features/reset_pass/pages/reset_pass_success_page.dart create mode 100644 packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_success_pass_change_layout.dart create mode 100644 packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_success_pass_change_layout_test.dart diff --git a/packages/deriv_auth_ui/example/lib/features/reset_pass/pages/choose_new_password_page.dart b/packages/deriv_auth_ui/example/lib/features/reset_pass/pages/choose_new_password_page.dart index 6ce9f1198..49dbbd6c9 100644 --- a/packages/deriv_auth_ui/example/lib/features/reset_pass/pages/choose_new_password_page.dart +++ b/packages/deriv_auth_ui/example/lib/features/reset_pass/pages/choose_new_password_page.dart @@ -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 { @@ -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 route) => false, ); }, onResetPassError: (_) {}, diff --git a/packages/deriv_auth_ui/example/lib/features/reset_pass/pages/reset_pass_success_page.dart b/packages/deriv_auth_ui/example/lib/features/reset_pass/pages/reset_pass_success_page.dart new file mode 100644 index 000000000..a0de6729d --- /dev/null +++ b/packages/deriv_auth_ui/example/lib/features/reset_pass/pages/reset_pass_success_page.dart @@ -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 createState() => _ResetPassSuccessPageState(); +} + +class _ResetPassSuccessPageState extends State { + 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( + >[ + Future.delayed(_successPageHoldDuration), + BlocProvider.of(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(); + } +} diff --git a/packages/deriv_auth_ui/lib/deriv_auth_ui.dart b/packages/deriv_auth_ui/lib/deriv_auth_ui.dart index 866264414..20c4c1f3e 100644 --- a/packages/deriv_auth_ui/lib/deriv_auth_ui.dart +++ b/packages/deriv_auth_ui/lib/deriv_auth_ui.dart @@ -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'; diff --git a/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_choose_new_pass_layout.dart b/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_choose_new_pass_layout.dart index 787a508d9..1d3f61a78 100644 --- a/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_choose_new_pass_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_choose_new_pass_layout.dart @@ -36,12 +36,9 @@ class DerivChooseNewPassLayout extends StatefulWidget { } class _DerivChooseNewPassLayoutState extends State { - static const Duration _successPageHoldDuration = Duration(seconds: 2); - final GlobalKey _formKey = GlobalKey(); final TextEditingController _passController = TextEditingController(); final FocusNode _passFocusNode = FocusNode(); - final PageController _pageController = PageController(); bool _isBusy = false; bool _isPasswordVisible = false; @@ -59,25 +56,12 @@ class _DerivChooseNewPassLayoutState extends State { body: BlocListener( listener: (BuildContext context, DerivResetPassState state) { if (state is DerivResetPassPasswordChangedState) { - _pageController.animateToPage( - 1, - duration: slidingPageChangeDuration, - curve: Curves.easeInOut, - ); - - Timer(_successPageHoldDuration, widget.onResetPassSucceed); + widget.onResetPassSucceed(); } else if (state is DerivResetPassErrorState) { widget.onResetPassError(state.errorMessage); } }, - child: PageView( - controller: _pageController, - physics: const NeverScrollableScrollPhysics(), - children: [ - _buildChooseNewPassSection(context), - _buildSuccessPassChangeSection(context) - ], - ), + child: _buildChooseNewPassSection(context), ), ); @@ -158,44 +142,6 @@ class _DerivChooseNewPassLayoutState extends State { ), ); - Widget _buildSuccessPassChangeSection(BuildContext context) => Center( - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: ThemeProvider.margin16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox( - height: ThemeProvider.margin16, - width: ThemeProvider.margin16, - child: LoadingIndicator( - valueColor: Colors.white, - strokeWidth: 2.5, - ), - ), - const SizedBox( - height: ThemeProvider.margin16, - ), - Text( - context.localization.informYourPassHasBeenReset, - style: TextStyles.title, - ), - const SizedBox( - height: ThemeProvider.margin08, - ), - Text( - context.localization.informRedirectLogin, - textAlign: TextAlign.center, - style: context.theme.textStyle( - textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, - ), - ), - ], - ), - ), - ); - Widget _buildSubmitPassButton() => ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all( @@ -252,7 +198,6 @@ class _DerivChooseNewPassLayoutState extends State { @override void dispose() { - _pageController.dispose(); _passController.dispose(); _passFocusNode.dispose(); diff --git a/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_success_pass_change_layout.dart b/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_success_pass_change_layout.dart new file mode 100644 index 000000000..b0af4c580 --- /dev/null +++ b/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_success_pass_change_layout.dart @@ -0,0 +1,59 @@ +import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; +import 'package:deriv_theme/deriv_theme.dart'; +import 'package:deriv_ui/presentation/widgets/loading_indicator.dart'; +import 'package:flutter/material.dart'; + +/// Success pass change page layout. +class DerivSuccessPassChangeLayout extends StatelessWidget { + /// Initializes success pass change page. + const DerivSuccessPassChangeLayout({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) => Scaffold( + backgroundColor: context.theme.colors.primary, + appBar: AppBar( + elevation: ThemeProvider.zeroMargin, + title: Text( + context.localization.labelResetPassword, + style: TextStyles.title, + ), + ), + body: Center( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: ThemeProvider.margin16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: ThemeProvider.margin16, + width: ThemeProvider.margin16, + child: LoadingIndicator( + valueColor: Colors.white, + strokeWidth: 2.5, + ), + ), + const SizedBox( + height: ThemeProvider.margin16, + ), + Text( + context.localization.informYourPassHasBeenReset, + style: TextStyles.title, + ), + const SizedBox( + height: ThemeProvider.margin08, + ), + Text( + context.localization.informRedirectLogin, + textAlign: TextAlign.center, + style: context.theme.textStyle( + textStyle: TextStyles.body1, + color: context.theme.colors.general, + ), + ), + ], + ), + ), + ), + ); +} diff --git a/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart b/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart index fecfca5db..7ee918392 100644 --- a/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart @@ -29,6 +29,7 @@ void main() { patrolWidgetTest('should render DerivGetStartedLayout', (PatrolTester $) async { await $.pumpApp(DerivGetStartedLayout( + onTapNavigation: () {}, slides: [mockSlideModel], appLogoIconPath: appLogoIconPath, backgroundImagePath: backgroundImagePath, @@ -47,6 +48,7 @@ void main() { bool loginTapped = false; await $.pumpApp(DerivGetStartedLayout( + onTapNavigation: () {}, slides: [mockSlideModel], appLogoIconPath: appLogoIconPath, backgroundImagePath: backgroundImagePath, @@ -66,6 +68,7 @@ void main() { bool signupTapped = false; await $.pumpApp(DerivGetStartedLayout( + onTapNavigation: () {}, slides: [mockSlideModel], appLogoIconPath: appLogoIconPath, backgroundImagePath: backgroundImagePath, diff --git a/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_success_pass_change_layout_test.dart b/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_success_pass_change_layout_test.dart new file mode 100644 index 000000000..9e647d321 --- /dev/null +++ b/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_success_pass_change_layout_test.dart @@ -0,0 +1,17 @@ +import 'package:deriv_auth_ui/deriv_auth_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:patrol_finders/patrol_finders.dart'; + +import '../../../pump_app.dart'; + +void main() { + group('DerivSuccessPassChangeLayout', () { + patrolWidgetTest('renders correctly', (PatrolTester $) async { + await $.pumpApp(settle: false, const DerivSuccessPassChangeLayout()); + + expect($(Text).$('Reset Password'), findsOneWidget); + expect($(Text).$('Your password has been reset'), findsOneWidget); + }); + }); +} From 4ba23ea9b791d5d1d6aaf54fd4562ba1232e4259 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Tue, 31 Oct 2023 17:57:53 +0800 Subject: [PATCH 17/44] docs: update documentation based on latest changes --- packages/deriv_auth_ui/README.md | 132 ++++++++++++++++++ .../src/core/states/auth_state_listener.dart | 3 +- 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/packages/deriv_auth_ui/README.md b/packages/deriv_auth_ui/README.md index 504ab5166..a7444646b 100644 --- a/packages/deriv_auth_ui/README.md +++ b/packages/deriv_auth_ui/README.md @@ -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 @@ -60,6 +117,9 @@ MaterialApp( backgroundImagePath: backgroundImagePath, onLoginTapped: () {}, onSignupTapped: () {}, + onTapNavigation: (){ + // Callback to be called when pressed on the screen seven times + }, ); ``` ### - Login Flow @@ -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 }, @@ -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) {}, @@ -140,6 +202,7 @@ MaterialApp( ``` dart DerivSetPasswordLayout( onDerivAuthState: (BuildContext, DerivAuthState) {}, + authErrorStateHandler: AuthErrorStateHandler(context: context), onDerivSignupState: (BuildContext, DerivSignupState) {}, onPreviousPressed: () {}, verificationCode: '123456', @@ -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 AuthStateListener extends StatelessWidget { + /// {@macro auth_state_listener} + const AuthStateListener({ + required this.child, + super.key, + this.onLoggedIn, + this.onLoggedOut, + this.onLoading, + this.onError, + this.authErrorStateHandler, + }); + + /// The [Widget] that is using this [AuthStateListener]. + 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( + 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, + ); +} + +``` \ No newline at end of file diff --git a/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart b/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart index 0e44d434e..a31a42e62 100644 --- a/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart +++ b/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart @@ -5,7 +5,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; /// {@template auth_state_listener} /// A [Widget] that listens to the [DerivAuthCubit] state changes. -/// This was created to make it easier to use [AuthErrorStateHandler]. +/// This was created to make it easier to use [AuthErrorStateHandler] by +/// handling the mapping of auth error states with [AuthErrorStateHandler]'s method'. /// {@endtemplate} class AuthStateListener extends StatelessWidget { /// {@macro auth_state_listener} From c2c7a80a5fe39f747897a8a6be09701d125716e9 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Thu, 2 Nov 2023 12:00:43 +0800 Subject: [PATCH 18/44] refactor: add eof and remove deprecated lint --- packages/deriv_auth_ui/analysis_options.yaml | 4 +--- packages/deriv_auth_ui/lib/l10n/intl_en.arb | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/deriv_auth_ui/analysis_options.yaml b/packages/deriv_auth_ui/analysis_options.yaml index 3d0c05ee4..2681030b5 100644 --- a/packages/deriv_auth_ui/analysis_options.yaml +++ b/packages/deriv_auth_ui/analysis_options.yaml @@ -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 diff --git a/packages/deriv_auth_ui/lib/l10n/intl_en.arb b/packages/deriv_auth_ui/lib/l10n/intl_en.arb index a08cd5a19..4c14905e1 100644 --- a/packages/deriv_auth_ui/lib/l10n/intl_en.arb +++ b/packages/deriv_auth_ui/lib/l10n/intl_en.arb @@ -105,4 +105,4 @@ "labelCountryConsentBrazil": "I hereby confirm that my request for opening an account with Deriv to trade OTC products issued and offered exclusively outside Brazil was initiated by me. I fully understand that Deriv is not regulated by CVM and by approaching Deriv I intend to set up a relation with a foreign company.", "informConnectionError": "Connection error. Please try again later.", "informSwitchAccountError": "Switch account error. Please try again later." -} \ No newline at end of file +} From e65d7587e334e82486f6510a50015784adf6fddd Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:54:09 +0800 Subject: [PATCH 19/44] refactor: make countries non-nullable --- .../signup/cubits/deriv_country_selection_state.dart | 8 ++++---- .../signup/layouts/deriv_country_selection_layout.dart | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart index 63f553b0e..b40add2d9 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart @@ -11,7 +11,7 @@ abstract class DerivCountrySelectionState { }); /// List of countries. - final List? countries; + final List countries; /// Selected country. final DerivResidenceModel? selectedCountry; @@ -38,7 +38,7 @@ class DerivCountrySelectionInitialState extends DerivCountrySelectionState { class DerivCountrySelectionLoadedState extends DerivCountrySelectionState { /// Initialise country list loaded state const DerivCountrySelectionLoadedState( - List? countries, { + List countries, { DerivResidenceModel? selectedCountry, }) : super(countries, selectedCountry: selectedCountry); } @@ -47,7 +47,7 @@ class DerivCountrySelectionLoadedState extends DerivCountrySelectionState { class DerivCountryChangedState extends DerivCountrySelectionState { /// Initialise country selection changed state. const DerivCountryChangedState( - List? countries, { + List countries, { DerivResidenceModel? selectedCountry, bool? selectedCountryRequiresConsent, }) : super( @@ -62,7 +62,7 @@ class DerivCountryChangedState extends DerivCountrySelectionState { class DerivCountryConsentChangedState extends DerivCountrySelectionState { /// Initialize country list loaded state const DerivCountryConsentChangedState( - List? countries, { + List countries, { DerivResidenceModel? selectedCountry, bool? selectedCountryRequiresConsent, bool? agreedToTerms, diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart index ee5c591c5..258cac866 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_country_selection_layout.dart @@ -139,8 +139,7 @@ class _DerivCountrySelectionLayoutState enabled: _shouldEnableCountrySelectionField(state), validator: (String? value) => _countrySelectionValidator(context, selectedCountry: state.selectedCountry), - onTap: () => - _onSelectCountryTap(state.countries ?? []), + onTap: () => _onSelectCountryTap(state.countries), ), ), ); From e88f6332844433953a63ff5933612ea2e115b045 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Fri, 10 Nov 2023 11:21:50 +0800 Subject: [PATCH 20/44] refactor: deriv country selection cubit --- .../cubits/deriv_country_selection_state.dart | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart index b40add2d9..ed48f1844 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_state.dart @@ -1,10 +1,10 @@ part of 'deriv_country_selection_cubit.dart'; /// Country selection state -abstract class DerivCountrySelectionState { +abstract class DerivCountrySelectionState extends Equatable { /// Initialize country selection state. - const DerivCountrySelectionState( - this.countries, { + const DerivCountrySelectionState({ + this.countries = const [], this.selectedCountry, this.agreedToTerms = false, this.selectedCountryRequiresConsent = false, @@ -25,13 +25,21 @@ abstract class DerivCountrySelectionState { /// If the selected country requires consent to continue, value is true. /// Default is false. final bool selectedCountryRequiresConsent; + + @override + List get props => [ + countries, + selectedCountry, + agreedToTerms, + selectedCountryRequiresConsent, + ]; } /// Initial state. class DerivCountrySelectionInitialState extends DerivCountrySelectionState { /// Initialises initial state. const DerivCountrySelectionInitialState() - : super(const []); + : super(countries: const []); } /// Country list loaded state. @@ -40,7 +48,7 @@ class DerivCountrySelectionLoadedState extends DerivCountrySelectionState { const DerivCountrySelectionLoadedState( List countries, { DerivResidenceModel? selectedCountry, - }) : super(countries, selectedCountry: selectedCountry); + }) : super(countries: countries, selectedCountry: selectedCountry); } /// Country selection changed state. @@ -51,7 +59,7 @@ class DerivCountryChangedState extends DerivCountrySelectionState { DerivResidenceModel? selectedCountry, bool? selectedCountryRequiresConsent, }) : super( - countries, + countries: countries, selectedCountry: selectedCountry, selectedCountryRequiresConsent: selectedCountryRequiresConsent ?? false, @@ -67,7 +75,7 @@ class DerivCountryConsentChangedState extends DerivCountrySelectionState { bool? selectedCountryRequiresConsent, bool? agreedToTerms, }) : super( - countries, + countries: countries, selectedCountry: selectedCountry, agreedToTerms: agreedToTerms ?? false, selectedCountryRequiresConsent: From 5b0d5f0c37ecd732739204d2a41c334779803945 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Fri, 10 Nov 2023 11:42:38 +0800 Subject: [PATCH 21/44] refactor: deriv country selection cubit --- .../features/signup/cubits/deriv_country_selection_cubit.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart index 9e0c29751..49a5d7a9a 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/cubits/deriv_country_selection_cubit.dart @@ -1,4 +1,5 @@ import 'package:deriv_auth_ui/src/core/helpers/country_selection_helper.dart'; +import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:deriv_auth_ui/src/features/signup/models/deriv_residence_model.dart'; From 5e6ddfbb26f8c630a9a8dc4339b706b06be9ce48 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Thu, 16 Nov 2023 15:12:11 +0800 Subject: [PATCH 22/44] ci: integrate github actions and setup melos (#328) --- .github/workflows/all_packages.yaml | 44 +++++++++++++++++++ .github/workflows/pr_title.yaml | 19 ++++++++ README.md | 37 ++++++++++++++++ melos.yaml | 42 ++++++++++++++++++ packages/deriv_auth_ui/example/pubspec.yaml | 2 +- packages/deriv_auth_ui/pubspec.yaml | 4 +- ...deriv_unavailable_country_layout_test.dart | 5 +-- .../deriv_get_started_layout_test.dart | 10 ++--- .../login/layouts/deriv_2fa_layout_test.dart | 15 +++---- .../layouts/deriv_login_layout_test.dart | 31 +++++++------ .../deriv_choose_new_pass_layout_test.dart | 13 +++--- .../layouts/deriv_reset_pass_layout_test.dart | 17 +++---- .../deriv_country_selection_layout_test.dart | 8 ++-- .../deriv_email_not_received_layout_test.dart | 8 ++-- .../deriv_set_password_layout_test.dart | 17 +++---- .../layouts/deriv_signup_layout_test.dart | 15 +++---- .../deriv_verification_done_layout_test.dart | 7 ++- .../deriv_verify_email_layout_test.dart | 8 ++-- .../models/deriv_auth_utm_model_test.dart | 9 ++-- .../deriv_password_policy_model_test.dart | 13 +++--- .../models/deriv_residence_model_test.dart | 4 +- .../country_selection_list_widget_test.dart | 12 ++--- packages/deriv_auth_ui/test/pump_app.dart | 2 +- packages/deriv_env/test/cipher_test.dart | 2 +- pubspec.yaml | 7 +++ 25 files changed, 253 insertions(+), 98 deletions(-) create mode 100644 .github/workflows/all_packages.yaml create mode 100644 .github/workflows/pr_title.yaml create mode 100644 melos.yaml create mode 100644 pubspec.yaml diff --git a/.github/workflows/all_packages.yaml b/.github/workflows/all_packages.yaml new file mode 100644 index 000000000..6e25797c2 --- /dev/null +++ b/.github/workflows/all_packages.yaml @@ -0,0 +1,44 @@ +name: all_packages + +on: + push: + branches: + - master + - dev + pull_request: + branches: + - "*" + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + analyze_and_test: + runs-on: ubuntu-latest + steps: + - name: Git Checkout + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: "3.10.2" + cache: true + + - name: Set SSH Key + uses: webfactory/ssh-agent@v0.8.0 + with: + ssh-private-key: ${{secrets.SSH_PRIVATE_KEY}} + + - name: Install Melos and run pub get + uses: bluefireteam/melos-action@v1 + with: + melos-version: "3.0.1" + + - name: Run Analyze + run: melos run analyze + + - name: Run Test + run: melos run test --no-select diff --git a/.github/workflows/pr_title.yaml b/.github/workflows/pr_title.yaml new file mode 100644 index 000000000..731c27830 --- /dev/null +++ b/.github/workflows/pr_title.yaml @@ -0,0 +1,19 @@ +name: pr_title + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +permissions: + pull-requests: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 5b70334a5..0be380907 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,40 @@ This repository contains private packages & plugins that are used by the company | [form_builder](./packages/form_builder) | A simpler and cleaner way to create, validate and submit forms. | | [update_checker](./packages/update_checker) | Check and retrieve update information from the server for the given package. | +## Environment Setup + +We use [Melos](https://pub.dev/packages/melos) to manage the multiple packages in this repository. To get started, install Melos globally: + +```bash +$ dart pub global activate melos +``` + +Running `pub get` on all packages, run: + +```bash +$ melos bootstrap +``` + +`Analyze` and `Test` has already been configured in `melos.yaml` so you can do the following: + +Running `flutter analyze` on all packages: + +```bash +$ melos run analyze +``` + +Running `flutter test` on all packages: + +```bash +$ melos run test --no-select +``` + +If you'd like to run any other command on all packages, you can configure it in `melos.yaml` or run it directly with melos: + +```bash +$ melos exec --\ + your command here +``` + + + diff --git a/melos.yaml b/melos.yaml new file mode 100644 index 000000000..2c94ec58c --- /dev/null +++ b/melos.yaml @@ -0,0 +1,42 @@ +name: flutter_deriv_packages +repository: https://github.com/regentmarkets/flutter-deriv-packages + +packages: + - packages/* + +command: + version: + # Generate commit links in package changelogs. + linkToCommits: true + workspaceChangelog: true + updateGitTagRefs: true + + bootstrap: + # It seems so that running "pub get" in parallel has some issues (like + # https://github.com/dart-lang/pub/issues/3404). Disabling this feature + # makes the CI much more stable. + runPubGetInParallel: false + +scripts: + analyze: + name: Flutter Analyze + description: Run flutter analyze for all packages. + ## Only fail if there are errors or warnings and not on infos. + run: | + if flutter analyze 2>&1 | grep -q -E 'error:|warning:'; + then exit 1 + else exit 0 + fi + exec: + concurrency: 1 + failFast: true + + test: + name: Flutter Test + description: Run flutter test for all packages. + run: flutter test --coverage + exec: + failFast: true + packageFilters: + dirExists: + - test diff --git a/packages/deriv_auth_ui/example/pubspec.yaml b/packages/deriv_auth_ui/example/pubspec.yaml index d72faeb0d..4c200936c 100644 --- a/packages/deriv_auth_ui/example/pubspec.yaml +++ b/packages/deriv_auth_ui/example/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_auth - ref: d5bef178341a318f911b42b8105d8c3d50c6b63e + ref: dev deriv_theme: path: ../../deriv_theme # git: diff --git a/packages/deriv_auth_ui/pubspec.yaml b/packages/deriv_auth_ui/pubspec.yaml index 0b7c361f6..32f15918a 100644 --- a/packages/deriv_auth_ui/pubspec.yaml +++ b/packages/deriv_auth_ui/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_auth - ref: d5bef178341a318f911b42b8105d8c3d50c6b63e + ref: dev deriv_theme: path: ../deriv_theme # git: @@ -40,7 +40,7 @@ dev_dependencies: sdk: flutter flutter_lints: ^2.0.0 mocktail: ^0.3.0 - patrol: ^1.1.4 + patrol_finders: ^1.0.0 bloc_test: ^9.1.3 flutter_intl: diff --git a/packages/deriv_auth_ui/test/core/layouts/deriv_unavailable_country_layout_test.dart b/packages/deriv_auth_ui/test/core/layouts/deriv_unavailable_country_layout_test.dart index ed7d7feda..667fc983e 100644 --- a/packages/deriv_auth_ui/test/core/layouts/deriv_unavailable_country_layout_test.dart +++ b/packages/deriv_auth_ui/test/core/layouts/deriv_unavailable_country_layout_test.dart @@ -1,12 +1,11 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_ui/presentation/widgets/fullscreen_message.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../base_test_app.dart'; void main() { - patrolTest('DerivUnavailableCountryLayout', (PatrolTester $) async { + patrolWidgetTest('DerivUnavailableCountryLayout', (PatrolTester $) async { await $.pumpWidgetAndSettle(BaseTestApp( child: DerivUnavailableCountryLayout( userAgent: '', diff --git a/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart b/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart index 82100761b..5acb0fd52 100644 --- a/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/get_started/layouts/deriv_get_started_layout_test.dart @@ -5,8 +5,7 @@ import 'package:deriv_ui/deriv_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; class MockDerivGetStartedSlideModel extends Mock @@ -26,7 +25,8 @@ void main() { when(() => mockSlideModel.supportingText).thenReturn('Supporting text'); }); - patrolTest('should render DerivGetStartedLayout', (PatrolTester $) async { + patrolWidgetTest('should render DerivGetStartedLayout', + (PatrolTester $) async { await $.pumpApp(DerivGetStartedLayout( slides: [mockSlideModel], appLogoIconPath: appLogoIconPath, @@ -41,7 +41,7 @@ void main() { expect($(SecondaryButton), findsOneWidget); }); - patrolTest('should call onLoginTapped when login button is pressed', + patrolWidgetTest('should call onLoginTapped when login button is pressed', (PatrolTester $) async { bool loginTapped = false; @@ -60,7 +60,7 @@ void main() { expect(loginTapped, isTrue); }); - patrolTest('should call onSignupTapped when signup button is pressed', + patrolWidgetTest('should call onSignupTapped when signup button is pressed', (PatrolTester $) async { bool signupTapped = false; diff --git a/packages/deriv_auth_ui/test/features/login/layouts/deriv_2fa_layout_test.dart b/packages/deriv_auth_ui/test/features/login/layouts/deriv_2fa_layout_test.dart index a835bd124..6a7aa9256 100644 --- a/packages/deriv_auth_ui/test/features/login/layouts/deriv_2fa_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/login/layouts/deriv_2fa_layout_test.dart @@ -8,8 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -25,7 +24,7 @@ void main() { mockPassword = 'test1234'; }); - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { when(() => authCubit.state).thenAnswer((_) => DerivAuthLoggedOutState()); when(() => authCubit.stream) @@ -44,7 +43,8 @@ void main() { expect($(ElevatedButton).$('Proceed'), findsOneWidget); }); - patrolTest('proceeds to login on correct code', (PatrolTester $) async { + patrolWidgetTest('proceeds to login on correct code', + (PatrolTester $) async { when(() => authCubit.state).thenAnswer((_) => DerivAuthLoggedOutState()); when(() => authCubit.stream) @@ -54,10 +54,9 @@ void main() { email: any(named: 'email'), password: any(named: 'password'), otp: any(named: 'otp'))) - .thenAnswer((_) async => DerivAuthLoggedInState( - authorizeEntity: const AuthorizeEntity(), - landingCompany: const LandingCompanyEntity(), - )); + .thenAnswer((_) async => DerivAuthLoggedInState(const DerivAuthModel( + authorizeEntity: AuthorizeEntity(), + landingCompany: LandingCompanyEntity()))); await $.pumpApp( BlocProvider.value( diff --git a/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart b/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart index 101e4be1e..e746f3a6b 100644 --- a/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/login/layouts/deriv_login_layout_test.dart @@ -9,8 +9,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -23,7 +22,7 @@ void main() { setUpAll(() => authCubit = MockAuthCubit()); - patrolTest( + patrolWidgetTest( 'renders email and password field including social auth buttons.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); @@ -54,7 +53,7 @@ void main() { expect($(DerivSocialAuthPanel), findsOneWidget); }); - patrolTest('displays invalid email error on invalid email typed.', + patrolWidgetTest('displays invalid email error on invalid email typed.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); const invalidEmail = 'invalid-email'; @@ -87,7 +86,7 @@ void main() { expect($(Text).$('Enter a valid email address'), findsOneWidget); }); - patrolTest('displays loading error on AuthLoadingState', + patrolWidgetTest('displays loading error on AuthLoadingState', (PatrolTester $) async { final mockAuthState = DerivAuthLoadingState(); @@ -114,7 +113,7 @@ void main() { expect($(LoadingIndicator), findsOneWidget); }); - patrolTest('calls signupTapped when signup button is pressed.', + patrolWidgetTest('calls signupTapped when signup button is pressed.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); @@ -149,10 +148,13 @@ void main() { expect(onSignupTappedCalled, isTrue); }); - patrolTest('calls onLoggedIn on successful login.', (PatrolTester $) async { + patrolWidgetTest('calls onLoggedIn on successful login.', + (PatrolTester $) async { final mockAuthState = DerivAuthLoggedInState( - authorizeEntity: const AuthorizeEntity(), - landingCompany: const LandingCompanyEntity(), + const DerivAuthModel( + authorizeEntity: AuthorizeEntity(), + landingCompany: LandingCompanyEntity(), + ), ); when(() => authCubit.state).thenAnswer((_) => mockAuthState); @@ -180,9 +182,12 @@ void main() { expect(onLoggedInCalled, isTrue); }); - patrolTest('calls onLoginError on login error.', (PatrolTester $) async { + patrolWidgetTest('calls onLoginError on login error.', + (PatrolTester $) async { final mockAuthState = DerivAuthErrorState( - message: 'error', type: AuthErrorType.failedAuthorization); + isSocialLogin: false, + message: 'error', + type: AuthErrorType.failedAuthorization); when(() => authCubit.state).thenAnswer((_) => mockAuthState); @@ -209,7 +214,7 @@ void main() { expect(onLoginErrorCalled, isTrue); }); - patrolTest('calls resetPassTapped when reset button is pressed.', + patrolWidgetTest('calls resetPassTapped when reset button is pressed.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); @@ -240,7 +245,7 @@ void main() { expect(onResetPassTappedCalled, isTrue); }); - patrolTest( + patrolWidgetTest( 'calls onSocialAuthButtonPressed when social auth button is pressed.', (PatrolTester $) async { final mockAuthState = DerivAuthLoggedOutState(); diff --git a/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_choose_new_pass_layout_test.dart b/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_choose_new_pass_layout_test.dart index b351286c8..895f3addb 100644 --- a/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_choose_new_pass_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_choose_new_pass_layout_test.dart @@ -7,8 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -21,7 +20,7 @@ void main() { mockResetPassCubit = MockDerivResetPassCubit(); }); - patrolTest('should render DerivChooseNewPassLayout', + patrolWidgetTest('should render DerivChooseNewPassLayout', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); @@ -43,7 +42,7 @@ void main() { expect($(DerivChooseNewPassLayout), findsOneWidget); }); - patrolTest( + patrolWidgetTest( 'should call onResetPassError when state is DerivResetPassErrorState', (PatrolTester $) async { const errorState = DerivResetPassErrorState(errorMessage: 'test error'); @@ -70,7 +69,7 @@ void main() { expect(onResetPassErrorCalled, isTrue); }); - patrolTest( + patrolWidgetTest( 'should call onResetPassSucceed after 2 seconds when state is DerivResetPassPasswordChangedState', (PatrolTester $) async { const passwordChangedState = DerivResetPassPasswordChangedState(); @@ -101,7 +100,7 @@ void main() { expect(onResetPassSucceedCalled, isTrue); }); - patrolTest('should call changePassword when input is valid', + patrolWidgetTest('should call changePassword when input is valid', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); const newPassTest = 'newPassWordTest123@'; @@ -131,7 +130,7 @@ void main() { token: token, newPassword: newPassTest)).called(1); }); - patrolTest('should not call changePassword when input is invalid', + patrolWidgetTest('should not call changePassword when input is invalid', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); const newPassTest = 'pass'; diff --git a/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_reset_pass_layout_test.dart b/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_reset_pass_layout_test.dart index 5f1df8405..743591f1e 100644 --- a/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_reset_pass_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/reset_pass/layouts/deriv_reset_pass_layout_test.dart @@ -7,8 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -20,7 +19,8 @@ void main() { mockResetPassCubit = MockDerivResetPassCubit(); }); - patrolTest('should render DerivResetPassLayout', (PatrolTester $) async { + patrolWidgetTest('should render DerivResetPassLayout', + (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); when(() => mockResetPassCubit.state).thenAnswer((_) => resetPassState); @@ -39,7 +39,7 @@ void main() { expect($(DerivResetPassLayout), findsOneWidget); }); - patrolTest( + patrolWidgetTest( 'should show emailSentPage when state is DerivResetPassEmailSentState', (PatrolTester $) async { const resetPassState = DerivResetPassEmailSentState(); @@ -63,7 +63,7 @@ void main() { findsOneWidget); }); - patrolTest( + patrolWidgetTest( 'should show submitEmailForm when state is DerivResetPassInitialState', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); @@ -87,7 +87,8 @@ void main() { findsOneWidget); }); - patrolTest('should not call sendVerificationEmail when email is invalid', + patrolWidgetTest( + 'should not call sendVerificationEmail when email is invalid', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); const email = 'wrongEmail@format'; @@ -115,7 +116,7 @@ void main() { verifyNever(() => mockResetPassCubit.sendVerificationEmail(email)); }); - patrolTest('should call sendVerificationEmail when email is valid', + patrolWidgetTest('should call sendVerificationEmail when email is valid', (PatrolTester $) async { const resetPassState = DerivResetPassInitialState(); const email = 'correctEmail@format.com'; @@ -145,7 +146,7 @@ void main() { verify(() => mockResetPassCubit.sendVerificationEmail(email)).called(1); }); - patrolTest( + patrolWidgetTest( 'should call OnResetPassError when state is DerivResetPassErrorState', (PatrolTester $) async { const errorState = DerivResetPassErrorState(errorMessage: 'test error'); diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_country_selection_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_country_selection_layout_test.dart index 6efff970b..9ded487c3 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_country_selection_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_country_selection_layout_test.dart @@ -2,8 +2,7 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_ui/deriv_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; void main() { @@ -13,13 +12,14 @@ void main() { setUp(() { residences = Future>.value([ - const DerivResidenceModel(code: 'ID', name: 'Indonesia', isDisabled: false), + const DerivResidenceModel( + code: 'ID', name: 'Indonesia', isDisabled: false), const DerivResidenceModel( code: 'UK', name: 'England', isDisabled: true), ]); }); - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp(DerivCountrySelectionLayout( onNextPressed: () {}, verificationCode: '123456', diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_email_not_received_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_email_not_received_layout_test.dart index 910b3b3fb..11cbcba05 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_email_not_received_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_email_not_received_layout_test.dart @@ -1,13 +1,12 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; void main() { group('DerivEmailNotReceivedLayout', () { - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp(DerivEmailNotReceivedLayout( onReEnterEmailPressed: () {}, )); @@ -17,7 +16,8 @@ void main() { findsOneWidget); }); - patrolTest('onReEnterEmailPressed is called when tapped on the button', + patrolWidgetTest( + 'onReEnterEmailPressed is called when tapped on the button', (PatrolTester $) async { bool isReEnterEmailPressed = false; diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart index 4e6ede2a4..f2d186cad 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_set_password_layout_test.dart @@ -7,8 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -31,7 +30,7 @@ void main() { }); group('DerivSetPasswordLayout', () { - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp( settle: false, MultiBlocProvider( @@ -53,7 +52,7 @@ void main() { expect($(ElevatedButton), findsNWidgets(2)); }); - patrolTest('onDerivAuthState is called on DerivAuth state changes', + patrolWidgetTest('onDerivAuthState is called on DerivAuth state changes', (PatrolTester $) async { bool isOnDerivAuthStateCalled = false; @@ -78,7 +77,8 @@ void main() { expect(isOnDerivAuthStateCalled, true); }); - patrolTest('onDerivSignupState is called on DerivSignup state changes', + patrolWidgetTest( + 'onDerivSignupState is called on DerivSignup state changes', (PatrolTester $) async { bool isOnDerivSignupStateCalled = false; @@ -106,7 +106,7 @@ void main() { expect(isOnDerivSignupStateCalled, true); }); - patrolTest('onPreviousPressed is called upon tapping previous button', + patrolWidgetTest('onPreviousPressed is called upon tapping previous button', (PatrolTester $) async { bool isOnPreviousPressedCalled = false; @@ -130,7 +130,8 @@ void main() { expect(isOnPreviousPressedCalled, true); }); - patrolTest('password is no longer obscure after visibility icon pressed', + patrolWidgetTest( + 'password is no longer obscure after visibility icon pressed', (PatrolTester $) async { await $.pumpApp( settle: false, @@ -158,7 +159,7 @@ void main() { findsOneWidget); }); - patrolTest('start trading button is disabled until password is valid', + patrolWidgetTest('start trading button is disabled until password is valid', (PatrolTester $) async { const validPassword = 'Abcdefg123'; diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart index c3186feb6..8a7f59f25 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_signup_layout_test.dart @@ -8,8 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../mocks.dart'; import '../../../pump_app.dart'; @@ -30,7 +29,7 @@ void main() { (_) => Stream.fromIterable([const DerivSignupInitialState()])); }); - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp( settle: false, BlocProvider.value( @@ -52,7 +51,7 @@ void main() { expect($(DerivSocialAuthPanel), findsOneWidget); }); - patrolTest( + patrolWidgetTest( 'onSocialAuthButtonPressed is called upon tapping social auth option', (PatrolTester $) async { bool isOnSocialAuthButtonPressedCalled = false; @@ -77,7 +76,7 @@ void main() { expect(isOnSocialAuthButtonPressedCalled, true); }); - patrolTest('onSignupEmailSent is called upon sign up email sent', + patrolWidgetTest('onSignupEmailSent is called upon sign up email sent', (PatrolTester $) async { bool isOnSignupEmailSentCalled = false; @@ -103,7 +102,7 @@ void main() { expect(isOnSignupEmailSentCalled, true); }); - patrolTest('onSignupPressed is called upon tapping signup button', + patrolWidgetTest('onSignupPressed is called upon tapping signup button', (PatrolTester $) async { bool isOnSignupPressedCalled = false; @@ -134,7 +133,7 @@ void main() { .called(1); }); - patrolTest('onLoginTapped is called upon tapping login button', + patrolWidgetTest('onLoginTapped is called upon tapping login button', (PatrolTester $) async { bool isOnLoginTappedCalled = false; @@ -161,7 +160,7 @@ void main() { expect(isOnLoginTappedCalled, true); }); - patrolTest('onSignupError is called upon signup error state', + patrolWidgetTest('onSignupError is called upon signup error state', (PatrolTester $) async { bool isOnSignupErrorCalled = false; diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verification_done_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verification_done_layout_test.dart index 36bcb5202..b36fd6719 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verification_done_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verification_done_layout_test.dart @@ -1,13 +1,12 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; void main() { group('DerivVerificationDoneLayout', () { - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp(DerivVerificationDoneLayout( verificationCode: '123456', onContinuePressed: () {}, @@ -18,7 +17,7 @@ void main() { expect($(ElevatedButton).$('Continue'), findsOneWidget); }); - patrolTest('onContinuePressed is called', (PatrolTester $) async { + patrolWidgetTest('onContinuePressed is called', (PatrolTester $) async { bool isContinuePressed = false; await $.pumpApp(DerivVerificationDoneLayout( diff --git a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verify_email_layout_test.dart b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verify_email_layout_test.dart index da35b53cc..3ba4740d3 100644 --- a/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verify_email_layout_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/layouts/deriv_verify_email_layout_test.dart @@ -1,8 +1,7 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; void main() { @@ -13,7 +12,7 @@ void main() { testEmail = 'test@gmail.com'; }); - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp(DerivVerifyEmailLayout( email: testEmail, onEmailNotReceivedPressed: () {}, @@ -25,7 +24,8 @@ void main() { expect($(Text).$('Check your email'), findsOneWidget); }); - patrolTest('onEmailNotReceivedPressed is called when tapped on the button', + patrolWidgetTest( + 'onEmailNotReceivedPressed is called when tapped on the button', (PatrolTester $) async { bool isEmailNotReceivedPressed = false; diff --git a/packages/deriv_auth_ui/test/features/signup/models/deriv_auth_utm_model_test.dart b/packages/deriv_auth_ui/test/features/signup/models/deriv_auth_utm_model_test.dart index daa546800..37d569f3c 100644 --- a/packages/deriv_auth_ui/test/features/signup/models/deriv_auth_utm_model_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/models/deriv_auth_utm_model_test.dart @@ -2,11 +2,11 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; void main() { group('DerivAuthUtmModel', () { - patrolTest('Constructor initializes all properties correctly', + patrolWidgetTest('Constructor initializes all properties correctly', (PatrolTester $) async { final model = DerivAuthUtmModel( affiliateToken: 'affiliateToken', @@ -31,9 +31,10 @@ void main() { expect(model.affiliateToken, 'affiliateToken'); }); - patrolTest('Constructor initializes optional properties as null', + patrolWidgetTest('Constructor initializes optional properties as null', (PatrolTester $) async { - final model = DerivAuthUtmModel(utmSource: 'source', utmCampaign: 'campaign'); + final model = + DerivAuthUtmModel(utmSource: 'source', utmCampaign: 'campaign'); expect(model.utmCampaignId, isNull); expect(model.utmAdGroupId, isNull); diff --git a/packages/deriv_auth_ui/test/features/signup/models/deriv_password_policy_model_test.dart b/packages/deriv_auth_ui/test/features/signup/models/deriv_password_policy_model_test.dart index 6a23e8b08..02515b627 100644 --- a/packages/deriv_auth_ui/test/features/signup/models/deriv_password_policy_model_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/models/deriv_password_policy_model_test.dart @@ -1,13 +1,13 @@ - // ignore_for_file: always_specify_types import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; void main() { group('DerivPasswordPolicyModel', () { - patrolTest('isMatchWith() returns true for matching password', (PatrolTester $) async { + patrolWidgetTest('isMatchWith() returns true for matching password', + (PatrolTester $) async { final policy = DerivPasswordPolicyModel( description: 'At least 8 characters', regex: RegExp(r'.{8,}'), @@ -16,7 +16,8 @@ void main() { expect(policy.isMatchWith('password123'), isTrue); }); - patrolTest('isMatchWith() returns false for non-matching password', (PatrolTester $) async { + patrolWidgetTest('isMatchWith() returns false for non-matching password', + (PatrolTester $) async { final policy = DerivPasswordPolicyModel( description: 'At least 8 characters', regex: RegExp(r'.{8,}'), @@ -25,7 +26,9 @@ void main() { expect(policy.isMatchWith('pass'), isFalse); }); - patrolTest('isMatchWith() returns true for optional policy with empty password', (PatrolTester $) async { + patrolWidgetTest( + 'isMatchWith() returns true for optional policy with empty password', + (PatrolTester $) async { final policy = DerivPasswordPolicyModel( description: 'Optional policy', regex: RegExp(r'^$'), diff --git a/packages/deriv_auth_ui/test/features/signup/models/deriv_residence_model_test.dart b/packages/deriv_auth_ui/test/features/signup/models/deriv_residence_model_test.dart index 9f1ca143a..e68a54495 100644 --- a/packages/deriv_auth_ui/test/features/signup/models/deriv_residence_model_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/models/deriv_residence_model_test.dart @@ -2,11 +2,11 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; void main() { group('DerivResidenceModel', () { - patrolTest('Constructor initializes all properties correctly', + patrolWidgetTest('Constructor initializes all properties correctly', (PatrolTester $) async { const model = DerivResidenceModel( isDisabled: true, diff --git a/packages/deriv_auth_ui/test/features/signup/widgets/country_selection_list_widget_test.dart b/packages/deriv_auth_ui/test/features/signup/widgets/country_selection_list_widget_test.dart index 54ac56f6f..5ba424b15 100644 --- a/packages/deriv_auth_ui/test/features/signup/widgets/country_selection_list_widget_test.dart +++ b/packages/deriv_auth_ui/test/features/signup/widgets/country_selection_list_widget_test.dart @@ -2,8 +2,7 @@ import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_auth_ui/src/features/signup/widgets/country_selection_list_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:patrol/patrol.dart'; - +import 'package:patrol_finders/patrol_finders.dart'; import '../../../pump_app.dart'; void main() { @@ -12,13 +11,14 @@ void main() { setUpAll(() { residences = [ - const DerivResidenceModel(code: 'ID', name: 'Indonesia', isDisabled: false), + const DerivResidenceModel( + code: 'ID', name: 'Indonesia', isDisabled: false), const DerivResidenceModel( code: 'UK', name: 'England', isDisabled: true), ]; }); - patrolTest('renders correctly', (PatrolTester $) async { + patrolWidgetTest('renders correctly', (PatrolTester $) async { await $.pumpApp( CountrySelectionListWidget(countries: residences, onChanged: (_) {})); @@ -28,7 +28,7 @@ void main() { expect($(Text).$('England'), findsOneWidget); }); - patrolTest('onChanged is called', (PatrolTester $) async { + patrolWidgetTest('onChanged is called', (PatrolTester $) async { bool isOnChangedCalled = false; await $.pumpApp(CountrySelectionListWidget( @@ -40,7 +40,7 @@ void main() { expect(isOnChangedCalled, true); }); - patrolTest('search field appears on tapping search', + patrolWidgetTest('search field appears on tapping search', (PatrolTester $) async { await $.pumpApp(CountrySelectionListWidget( countries: residences, onChanged: (int country) => null)); diff --git a/packages/deriv_auth_ui/test/pump_app.dart b/packages/deriv_auth_ui/test/pump_app.dart index 3f74dd32a..a290d6116 100644 --- a/packages/deriv_auth_ui/test/pump_app.dart +++ b/packages/deriv_auth_ui/test/pump_app.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/patrol_finders.dart'; import 'base_test_app.dart'; diff --git a/packages/deriv_env/test/cipher_test.dart b/packages/deriv_env/test/cipher_test.dart index 475facd4c..892633dba 100644 --- a/packages/deriv_env/test/cipher_test.dart +++ b/packages/deriv_env/test/cipher_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:deriv_env/Cipher.dart'; +import 'package:deriv_env/cipher.dart'; void main() { group('cipher tests =>', () { diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 000000000..9c29996ac --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,7 @@ +name: deriv_packages_workspace + +environment: + sdk: ">=2.18.0 <4.0.0" + +dev_dependencies: + melos: ^3.0.1 From b05830adfa0c5e0bb2bea4123c71be98e48d4101 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:32:31 +0800 Subject: [PATCH 23/44] chore: rename `AuthStateListener` --- packages/deriv_auth_ui/README.md | 6 +++--- .../lib/src/core/states/auth_state_listener.dart | 6 +++--- .../signup/layouts/deriv_set_password_layout.dart | 2 +- .../src/features/signup/layouts/deriv_signup_layout.dart | 2 +- .../test/core/states/auth_state_listener_test.dart | 8 ++++---- packages/deriv_bloc_manager/README.md | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/deriv_auth_ui/README.md b/packages/deriv_auth_ui/README.md index a7444646b..c98babcc0 100644 --- a/packages/deriv_auth_ui/README.md +++ b/packages/deriv_auth_ui/README.md @@ -241,9 +241,9 @@ The package handles the mapping of the error state to the corresponding method i ```dart -class AuthStateListener extends StatelessWidget { +class DerivAuthStateListener extends StatelessWidget { /// {@macro auth_state_listener} - const AuthStateListener({ + const DerivAuthStateListener({ required this.child, super.key, this.onLoggedIn, @@ -253,7 +253,7 @@ class AuthStateListener extends StatelessWidget { this.authErrorStateHandler, }); - /// The [Widget] that is using this [AuthStateListener]. + /// The [Widget] that is using this [DerivAuthStateListener]. final Widget child; /// Callback to be called when user is logged in. diff --git a/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart b/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart index a31a42e62..eee0ac6d6 100644 --- a/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart +++ b/packages/deriv_auth_ui/lib/src/core/states/auth_state_listener.dart @@ -8,9 +8,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; /// This was created to make it easier to use [AuthErrorStateHandler] by /// handling the mapping of auth error states with [AuthErrorStateHandler]'s method'. /// {@endtemplate} -class AuthStateListener extends StatelessWidget { +class DerivAuthStateListener extends StatelessWidget { /// {@macro auth_state_listener} - const AuthStateListener({ + const DerivAuthStateListener({ required this.child, super.key, this.onLoggedIn, @@ -20,7 +20,7 @@ class AuthStateListener extends StatelessWidget { this.authErrorStateHandler, }); - /// The [Widget] that is using this [AuthStateListener]. + /// The [Widget] that is using this [DerivAuthStateListener]. final Widget child; /// Callback to be called when user is logged in. diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart index 013bf03d7..63e5e359f 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_set_password_layout.dart @@ -68,7 +68,7 @@ class _DerivSetPasswordLayoutState extends State { } @override - Widget build(BuildContext context) => AuthStateListener( + Widget build(BuildContext context) => DerivAuthStateListener( authErrorStateHandler: widget.authErrorStateHandler, onError: widget.onAuthError, child: Scaffold( diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart index 7bd2ffa7b..bf836b483 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart @@ -89,7 +89,7 @@ class _DerivSignupLayoutState extends State { Text(context.localization.labelSignUp, style: TextStyles.title), backgroundColor: context.theme.colors.secondary, ), - body: AuthStateListener( + body: DerivAuthStateListener( authErrorStateHandler: widget.authErrorStateHandler, onError: widget.onAuthError, child: BlocConsumer( diff --git a/packages/deriv_auth_ui/test/core/states/auth_state_listener_test.dart b/packages/deriv_auth_ui/test/core/states/auth_state_listener_test.dart index bfcfdb067..85db400ae 100644 --- a/packages/deriv_auth_ui/test/core/states/auth_state_listener_test.dart +++ b/packages/deriv_auth_ui/test/core/states/auth_state_listener_test.dart @@ -41,7 +41,7 @@ void main() { await $.pumpApp( BlocProvider( create: (_) => authCubit, - child: AuthStateListener( + child: DerivAuthStateListener( onLoggedIn: (_) { isOnLoggedInCalled = true; }, @@ -66,7 +66,7 @@ void main() { await $.pumpApp( BlocProvider( create: (_) => authCubit, - child: AuthStateListener( + child: DerivAuthStateListener( onLoggedOut: () { isOnLoggedOutCalled = true; }, @@ -91,7 +91,7 @@ void main() { await $.pumpApp( BlocProvider( create: (_) => authCubit, - child: AuthStateListener( + child: DerivAuthStateListener( onLoading: () { isOnLoadingCalled = true; }, @@ -120,7 +120,7 @@ void main() { await $.pumpApp( BlocProvider( create: (_) => authCubit, - child: AuthStateListener( + child: DerivAuthStateListener( onError: (_) { isOnErrorCalled = true; }, diff --git a/packages/deriv_bloc_manager/README.md b/packages/deriv_bloc_manager/README.md index fecfc8c77..11b77964b 100644 --- a/packages/deriv_bloc_manager/README.md +++ b/packages/deriv_bloc_manager/README.md @@ -51,7 +51,7 @@ This section shows how to create a new bloc/cubit following the proposed archite ```dart abstract class BaseStateListener {} -abstract class AuthStateListener implements BaseStateListener { +abstract class DerivAuthStateListener implements BaseStateListener { void onLogin(Authorize authorizedAccount); void onLogout(); @@ -64,7 +64,7 @@ abstract class ConnectivityStateListener implements BaseStateListener { } ``` -2- Create a cubit for a feature, let’s call it `FeatureCubit`. The cubit class will implement both `AuthStateListener` and `ConnectivityStateListener` so it can expose the 4 methods in addition to any other feature-specific states. The type of the state `FeatureCubit` is managing in this example, is **Status** with initial value as `initial`; +2- Create a cubit for a feature, let’s call it `FeatureCubit`. The cubit class will implement both `DerivAuthStateListener` and `ConnectivityStateListener` so it can expose the 4 methods in addition to any other feature-specific states. The type of the state `FeatureCubit` is managing in this example, is **Status** with initial value as `initial`; ```dart enum Status { @@ -82,7 +82,7 @@ The `FeatureCubit` will expose the common/share `onConnect`, `onDisconnect`, `on ```dart import 'package:bloc/bloc.dart'; -class FeatureCubit extends Cubit implements ConnectivityStateListener, AuthStateListener { +class FeatureCubit extends Cubit implements ConnectivityStateListener, DerivAuthStateListener { FeatureCubit() : super(Status.initial); void loading() => emit(Status.loading); From cac78e49f1650fe1ba5f7698b97ce7a5adaa1308 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:34:18 +0800 Subject: [PATCH 24/44] fix: change color to general --- .../get_started/layouts/deriv_get_started_layout.dart | 2 +- .../src/features/login/layouts/deriv_2fa_layout.dart | 2 +- .../src/features/login/layouts/deriv_login_layout.dart | 4 ++-- .../login/widgets/deriv_social_auth_divider.dart | 2 +- .../reset_pass/layouts/deriv_reset_pass_layout.dart | 4 ++-- .../layouts/deriv_email_not_received_layout.dart | 10 +++++----- .../features/signup/layouts/deriv_signup_layout.dart | 4 ++-- .../signup/layouts/deriv_verification_done_layout.dart | 2 +- .../signup/layouts/deriv_verify_email_layout.dart | 2 +- .../signup/widgets/country_selection_list_widget.dart | 4 ++-- .../signup/widgets/password_policy_checker_widget.dart | 6 +++--- .../lib/src/date_range_picker.dart | 2 +- .../lib/src/widgets/date_range_text_field.dart | 2 +- .../lib/src/widgets/input_date_range.dart | 2 +- .../lib/src/widgets/month_item.dart | 4 ++-- .../deriv_numpad/lib/core/widgets/custom_checkbox.dart | 2 +- .../lib/presentation/widgets/base_text_field.dart | 8 +++----- .../lib/presentation/widgets/custom_checkbox.dart | 2 +- packages/deriv_ui/lib/utils/popup_dialogs_helper.dart | 8 ++++---- 19 files changed, 35 insertions(+), 37 deletions(-) diff --git a/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart b/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart index dbca53412..c074202e0 100644 --- a/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart @@ -227,7 +227,7 @@ class _DerivGetStartedLayoutState extends State { supportingText, style: context.theme.textStyle( textStyle: TextStyles.title, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), textAlign: TextAlign.center, ), diff --git a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_2fa_layout.dart b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_2fa_layout.dart index 00009622c..4634aaef3 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_2fa_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_2fa_layout.dart @@ -63,7 +63,7 @@ class _Deriv2FALayoutState extends State { context.localization.informEnterTwoFactorAuthCode, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), textAlign: TextAlign.center, ), diff --git a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart index 30187be4a..5e8fe6c70 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart @@ -143,7 +143,7 @@ class _DerivLoginLayoutState extends State { widget.greetingLabel, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ]; @@ -245,7 +245,7 @@ class _DerivLoginLayoutState extends State { context.localization.labelDontHaveAnAccountYet, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), InkWell( diff --git a/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_divider.dart b/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_divider.dart index 64d14f557..76f75bd72 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_divider.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_divider.dart @@ -23,7 +23,7 @@ class DerivSocialAuthDivider extends StatelessWidget { label, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), diff --git a/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_reset_pass_layout.dart b/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_reset_pass_layout.dart index 4614866aa..1182ead53 100644 --- a/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_reset_pass_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_reset_pass_layout.dart @@ -95,7 +95,7 @@ class _DerivResetPassLayoutState extends State { textAlign: TextAlign.center, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), const SizedBox(height: kToolbarHeight), @@ -129,7 +129,7 @@ class _DerivResetPassLayoutState extends State { context.localization.informResetPassByEmail, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_email_not_received_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_email_not_received_layout.dart index bad1e2728..a7dc2d4ec 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_email_not_received_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_email_not_received_layout.dart @@ -45,7 +45,7 @@ class DerivEmailNotReceivedLayout extends StatelessWidget { context.localization.labelEmailIssueHeader, style: context.theme.textStyle( textStyle: TextStyles.body2, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), const SizedBox(height: ThemeProvider.margin24), @@ -58,7 +58,7 @@ class DerivEmailNotReceivedLayout extends StatelessWidget { context.localization.labelEmailIssueSpam, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), @@ -74,7 +74,7 @@ class DerivEmailNotReceivedLayout extends StatelessWidget { context.localization.labelEmailIssueWrongEmail, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), @@ -90,7 +90,7 @@ class DerivEmailNotReceivedLayout extends StatelessWidget { context.localization.labelEmailIssueTypo, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), @@ -106,7 +106,7 @@ class DerivEmailNotReceivedLayout extends StatelessWidget { context.localization.labelEmailIssueFirewall, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart index bf836b483..c8b06d4d4 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart @@ -233,7 +233,7 @@ class _DerivSignupLayoutState extends State { widget.signupPageDescription, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ]; @@ -246,7 +246,7 @@ class _DerivSignupLayoutState extends State { context.localization.labelHaveAccount, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), InkWell( diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_verification_done_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_verification_done_layout.dart index 4df6eb34b..400d1e1f4 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_verification_done_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_verification_done_layout.dart @@ -64,7 +64,7 @@ class DerivVerificationDoneLayout extends StatelessWidget { context.localization.informLetsContinue, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ], diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_verify_email_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_verify_email_layout.dart index 46a525d8b..637f61365 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_verify_email_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_verify_email_layout.dart @@ -64,7 +64,7 @@ class DerivVerifyEmailLayout extends StatelessWidget { context.localization.informVerificationEmailSent(email!), style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), textAlign: TextAlign.center, ), diff --git a/packages/deriv_auth_ui/lib/src/features/signup/widgets/country_selection_list_widget.dart b/packages/deriv_auth_ui/lib/src/features/signup/widgets/country_selection_list_widget.dart index 0e4fa9133..294aa9aad 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/widgets/country_selection_list_widget.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/widgets/country_selection_list_widget.dart @@ -113,7 +113,7 @@ class _CountrySelectionListWidgetState focusNode: _searchFocusNode, style: context.theme.textStyle( textStyle: TextStyles.subheading, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), decoration: InputDecoration( border: InputBorder.none, @@ -145,7 +145,7 @@ class _CountrySelectionListWidgetState _filteredCountries[index].name, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), diff --git a/packages/deriv_auth_ui/lib/src/features/signup/widgets/password_policy_checker_widget.dart b/packages/deriv_auth_ui/lib/src/features/signup/widgets/password_policy_checker_widget.dart index 68dc90052..508f8eb8b 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/widgets/password_policy_checker_widget.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/widgets/password_policy_checker_widget.dart @@ -69,7 +69,7 @@ class PasswordPolicyCheckerWidget extends StatelessWidget { context.localization.informPasswordPolicy, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), const SizedBox(height: ThemeProvider.margin04), @@ -114,7 +114,7 @@ class PasswordPolicyCheckerWidget extends StatelessWidget { textStyle: TextStyles.body1, color: policy.isMatchWith(password) ? context.theme.colors.hover - : context.theme.colors.lessProminent, + : context.theme.colors.general, ); Widget _buildPolicyIcon({ @@ -129,7 +129,7 @@ class PasswordPolicyCheckerWidget extends StatelessWidget { policy.isMatchWith(password) ? Icons.check : Icons.circle; final Color color = policy.isMatchWith(password) - ? context.theme.colors.lessProminent + ? context.theme.colors.general : context.theme.colors.coral; return Padding( diff --git a/packages/deriv_date_range_picker/lib/src/date_range_picker.dart b/packages/deriv_date_range_picker/lib/src/date_range_picker.dart index f52a31216..3d5923708 100644 --- a/packages/deriv_date_range_picker/lib/src/date_range_picker.dart +++ b/packages/deriv_date_range_picker/lib/src/date_range_picker.dart @@ -237,7 +237,7 @@ class _DerivDateRangePickerState extends State { context.localization!.labelSelectedDateRange, style: context.theme.textStyle( textStyle: TextStyles.overline, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), diff --git a/packages/deriv_date_range_picker/lib/src/widgets/date_range_text_field.dart b/packages/deriv_date_range_picker/lib/src/widgets/date_range_text_field.dart index 0aa9a69bc..49440cca3 100644 --- a/packages/deriv_date_range_picker/lib/src/widgets/date_range_text_field.dart +++ b/packages/deriv_date_range_picker/lib/src/widgets/date_range_text_field.dart @@ -109,7 +109,7 @@ class _DateRangeTextFieldState extends State<_DateRangeTextField> { : TextInputAction.done, style: context.theme.textStyle( textStyle: TextStyles.subheading, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), decoration: InputDecoration( enabledBorder: OutlineInputBorder( diff --git a/packages/deriv_date_range_picker/lib/src/widgets/input_date_range.dart b/packages/deriv_date_range_picker/lib/src/widgets/input_date_range.dart index 9676a7bdf..50111d233 100644 --- a/packages/deriv_date_range_picker/lib/src/widgets/input_date_range.dart +++ b/packages/deriv_date_range_picker/lib/src/widgets/input_date_range.dart @@ -124,7 +124,7 @@ class _InputDateRangeState extends State { context.localization!.labelSelectedDateRange, style: context.theme.textStyle( textStyle: TextStyles.overline, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), diff --git a/packages/deriv_date_range_picker/lib/src/widgets/month_item.dart b/packages/deriv_date_range_picker/lib/src/widgets/month_item.dart index d9e6f5338..07a9167d7 100644 --- a/packages/deriv_date_range_picker/lib/src/widgets/month_item.dart +++ b/packages/deriv_date_range_picker/lib/src/widgets/month_item.dart @@ -157,7 +157,7 @@ class _MonthItemState extends State<_MonthItem> { localizations.formatMonthYear(widget.displayedMonth), style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), @@ -197,7 +197,7 @@ class _MonthItemState extends State<_MonthItem> { TextStyle itemStyle = context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ); BoxDecoration? decoration; diff --git a/packages/deriv_numpad/lib/core/widgets/custom_checkbox.dart b/packages/deriv_numpad/lib/core/widgets/custom_checkbox.dart index 4f0b4938a..5a7476e56 100644 --- a/packages/deriv_numpad/lib/core/widgets/custom_checkbox.dart +++ b/packages/deriv_numpad/lib/core/widgets/custom_checkbox.dart @@ -50,7 +50,7 @@ class CustomCheckbox extends StatelessWidget { textAlign: TextAlign.left, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), ), diff --git a/packages/deriv_ui/lib/presentation/widgets/base_text_field.dart b/packages/deriv_ui/lib/presentation/widgets/base_text_field.dart index f50a1b9f1..60a9ee1c9 100644 --- a/packages/deriv_ui/lib/presentation/widgets/base_text_field.dart +++ b/packages/deriv_ui/lib/presentation/widgets/base_text_field.dart @@ -156,8 +156,7 @@ class _BaseTextFieldState extends State { ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( - color: - widget.focusedBorderColor ?? context.theme.colors.blue, + color: widget.focusedBorderColor ?? context.theme.colors.blue, ), borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), ), @@ -174,8 +173,7 @@ class _BaseTextFieldState extends State { color: _hasError ? context.theme.colors.coral : _hasFocus() - ? widget.focusedLabelColor ?? - context.theme.colors.blue + ? widget.focusedLabelColor ?? context.theme.colors.blue : widget.labelColor ?? context.theme.colors.disabled, ), counterText: widget.showCounterText ? null : '', @@ -197,7 +195,7 @@ class _BaseTextFieldState extends State { Color _getTextFieldColor() => widget.enabled && _hasFocus() ? widget.focusedTextColor ?? context.theme.colors.prominent - : widget.textColor ?? context.theme.colors.lessProminent; + : widget.textColor ?? context.theme.colors.general; String? _validator(String? input) { final String? errorMsg = widget.validator?.call(input); diff --git a/packages/deriv_ui/lib/presentation/widgets/custom_checkbox.dart b/packages/deriv_ui/lib/presentation/widgets/custom_checkbox.dart index 31a6391d3..352ec2afb 100644 --- a/packages/deriv_ui/lib/presentation/widgets/custom_checkbox.dart +++ b/packages/deriv_ui/lib/presentation/widgets/custom_checkbox.dart @@ -62,7 +62,7 @@ class CustomCheckbox extends StatelessWidget { textAlign: TextAlign.left, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), onTap: onValueChanged == null diff --git a/packages/deriv_ui/lib/utils/popup_dialogs_helper.dart b/packages/deriv_ui/lib/utils/popup_dialogs_helper.dart index c70e69b7d..bb5e11de1 100644 --- a/packages/deriv_ui/lib/utils/popup_dialogs_helper.dart +++ b/packages/deriv_ui/lib/utils/popup_dialogs_helper.dart @@ -138,7 +138,7 @@ Future showSimpleLoadingDialog( bodyMessage, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), textAlign: TextAlign.center, ), @@ -172,7 +172,7 @@ Future showErrorDialog({ errorMessage ?? '', style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), positiveActionLabel: actionLabel, @@ -195,7 +195,7 @@ Future showTokenExpiredDialog({ content, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), ), positiveActionLabel: positiveActionLabel, @@ -223,7 +223,7 @@ Future showAccountDeactivatedDialog({ text: content, style: context.theme.textStyle( textStyle: TextStyles.body1, - color: context.theme.colors.lessProminent, + color: context.theme.colors.general, ), children: [ buildTextSpanHyperlink( From 411b2115d0f891b61036e601c5f26045e3a861f3 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:55:50 +0800 Subject: [PATCH 25/44] fix: reset password button alignment --- .../reset_pass/layouts/deriv_choose_new_pass_layout.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_choose_new_pass_layout.dart b/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_choose_new_pass_layout.dart index 1d3f61a78..5785745f8 100644 --- a/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_choose_new_pass_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_choose_new_pass_layout.dart @@ -76,7 +76,7 @@ class _DerivChooseNewPassLayoutState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: ThemeProvider.margin72), - Expanded(child: _buildContent()), + _buildContent(), const SizedBox(height: ThemeProvider.margin24), _buildSubmitPassButton() ], From 086fc636bdae24da873a9931e7b053b50e6b1a73 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Fri, 17 Nov 2023 17:42:27 +0800 Subject: [PATCH 26/44] fix: add missing ok label in referal dialog --- .../deriv_auth_ui/lib/generated/intl/messages_en.dart | 1 + packages/deriv_auth_ui/lib/generated/l10n.dart | 10 ++++++++++ packages/deriv_auth_ui/lib/l10n/intl_en.arb | 1 + .../features/signup/layouts/deriv_signup_layout.dart | 1 + 4 files changed, 13 insertions(+) diff --git a/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart b/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart index 258c5c064..c45e05536 100644 --- a/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart +++ b/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart @@ -43,6 +43,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Get a free account"), "actionLogin": MessageLookupByLibrary.simpleMessage("Log in"), "actionNext": MessageLookupByLibrary.simpleMessage("Next"), + "actionOk": MessageLookupByLibrary.simpleMessage("OK"), "actionPrevious": MessageLookupByLibrary.simpleMessage("Previous"), "actionProceed": MessageLookupByLibrary.simpleMessage("Proceed"), "actionReenterEmail": MessageLookupByLibrary.simpleMessage( diff --git a/packages/deriv_auth_ui/lib/generated/l10n.dart b/packages/deriv_auth_ui/lib/generated/l10n.dart index 10f1c1aaf..9426718ce 100644 --- a/packages/deriv_auth_ui/lib/generated/l10n.dart +++ b/packages/deriv_auth_ui/lib/generated/l10n.dart @@ -71,6 +71,16 @@ class DerivAuthUILocalization { ); } + /// `OK` + String get actionOk { + return Intl.message( + 'OK', + name: 'actionOk', + desc: '', + args: [], + ); + } + /// `If you have any questions, contact us via ` String get warnNotAvailableCountries { return Intl.message( diff --git a/packages/deriv_auth_ui/lib/l10n/intl_en.arb b/packages/deriv_auth_ui/lib/l10n/intl_en.arb index 4c14905e1..bb220ebc7 100644 --- a/packages/deriv_auth_ui/lib/l10n/intl_en.arb +++ b/packages/deriv_auth_ui/lib/l10n/intl_en.arb @@ -10,6 +10,7 @@ } } }, + "actionOk": "OK", "warnNotAvailableCountries": "If you have any questions, contact us via ", "labelLiveChat": "Live chat", "actionGetAFreeAccount": "Get a free account", diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart index c8b06d4d4..df51a4387 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart @@ -152,6 +152,7 @@ class _DerivSignupLayoutState extends State { dialogTitle: context.localization.labelReferralInfoTitle, dialogDescription: context.localization.infoReferralInfoDescription, + positiveActionLabel: context.localization.actionOk, iconSize: ThemeProvider.iconSize24, ), const SizedBox(width: ThemeProvider.margin08), From 016f3902d6d110f76446a0cc4ce0edfa2f35dd8a Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Fri, 17 Nov 2023 17:58:59 +0800 Subject: [PATCH 27/44] feat: add flag for social login --- .../login/layouts/deriv_login_layout.dart | 6 ++++ .../widgets/deriv_social_auth_divider.dart | 36 +++++++++++-------- .../signup/layouts/deriv_signup_layout.dart | 6 ++++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart index 5e8fe6c70..9411180cb 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart @@ -21,6 +21,7 @@ class DerivLoginLayout extends StatefulWidget { required this.onSocialAuthButtonPressed, required this.welcomeLabel, required this.greetingLabel, + this.isSocialAuthEnabled = true, this.authErrorStateHandler, this.onLoginError, this.onLoginTapped, @@ -54,6 +55,9 @@ class DerivLoginLayout extends StatefulWidget { /// Greeting text to be displayed on login page. final String greetingLabel; + /// Whether to display social auth buttons. + final bool isSocialAuthEnabled; + @override State createState() => _DerivLoginLayoutState(); } @@ -113,11 +117,13 @@ class _DerivLoginLayoutState extends State { const SizedBox(height: ThemeProvider.margin24), DerivSocialAuthDivider( label: context.localization.informLoginOptions, + isVisible: widget.isSocialAuthEnabled, ), const SizedBox(height: ThemeProvider.margin24), DerivSocialAuthPanel( onSocialAuthButtonPressed: widget.onSocialAuthButtonPressed, + isEnabled: widget.isSocialAuthEnabled, ), const SizedBox(height: ThemeProvider.margin24), _buildFooterSection(), diff --git a/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_divider.dart b/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_divider.dart index 76f75bd72..1f9411517 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_divider.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/widgets/deriv_social_auth_divider.dart @@ -6,29 +6,37 @@ class DerivSocialAuthDivider extends StatelessWidget { /// Initializes the class. const DerivSocialAuthDivider({ required this.label, + this.isVisible = true, Key? key, }) : super(key: key); /// The label that displayed in the divider. final String label; + /// Whether the buttons are visible. + /// Defaults to `true`. Acts as a flag to hide the buttons. + final bool isVisible; + @override - Widget build(BuildContext context) => Row( - children: [ - _buildDivider(context), - Padding( - padding: - const EdgeInsets.symmetric(horizontal: ThemeProvider.margin08), - child: Text( - label, - style: context.theme.textStyle( - textStyle: TextStyles.body1, - color: context.theme.colors.general, + Widget build(BuildContext context) => Visibility( + visible: isVisible, + child: Row( + children: [ + _buildDivider(context), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: ThemeProvider.margin08), + child: Text( + label, + style: context.theme.textStyle( + textStyle: TextStyles.body1, + color: context.theme.colors.general, + ), ), ), - ), - _buildDivider(context), - ], + _buildDivider(context), + ], + ), ); Widget _buildDivider(BuildContext context) => Expanded( diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart index df51a4387..a09037eda 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart @@ -24,6 +24,7 @@ class DerivSignupLayout extends StatefulWidget { required this.onLoginTapped, required this.signupPageLabel, required this.signupPageDescription, + this.isSocialAuthEnabled = true, this.authErrorStateHandler, this.enableReferralSection = true, this.onAuthError, @@ -61,6 +62,9 @@ class DerivSignupLayout extends StatefulWidget { /// Description of signup page. final String signupPageDescription; + /// Whether to display social auth buttons. + final bool isSocialAuthEnabled; + @override State createState() => _DerivSignupLayoutState(); } @@ -115,12 +119,14 @@ class _DerivSignupLayoutState extends State { const SizedBox(height: ThemeProvider.margin24), DerivSocialAuthDivider( label: context.localization.labelOrSignUpWith, + isVisible: widget.isSocialAuthEnabled, ), const SizedBox(height: ThemeProvider.margin24), DerivSocialAuthPanel( isEnabled: !isReferralEnabled, onSocialAuthButtonPressed: widget.onSocialAuthButtonPressed, + isVisible: widget.isSocialAuthEnabled, ), const SizedBox(height: ThemeProvider.margin24), _buildFooterSection(), From d782c86a69eae3489e261060c7eacf63eb2da9d0 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:18:34 +0800 Subject: [PATCH 28/44] fix: spacing after social flag --- .../src/features/login/layouts/deriv_login_layout.dart | 8 +++++--- .../src/features/signup/layouts/deriv_signup_layout.dart | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart index 9411180cb..0bdb7ae3d 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart @@ -119,13 +119,15 @@ class _DerivLoginLayoutState extends State { label: context.localization.informLoginOptions, isVisible: widget.isSocialAuthEnabled, ), - const SizedBox(height: ThemeProvider.margin24), + if (widget.isSocialAuthEnabled) + const SizedBox(height: ThemeProvider.margin24), DerivSocialAuthPanel( onSocialAuthButtonPressed: widget.onSocialAuthButtonPressed, - isEnabled: widget.isSocialAuthEnabled, + isVisible: widget.isSocialAuthEnabled, ), - const SizedBox(height: ThemeProvider.margin24), + if (widget.isSocialAuthEnabled) + const SizedBox(height: ThemeProvider.margin24), _buildFooterSection(), ], ), diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart index a09037eda..2a34b8396 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart @@ -121,14 +121,16 @@ class _DerivSignupLayoutState extends State { label: context.localization.labelOrSignUpWith, isVisible: widget.isSocialAuthEnabled, ), - const SizedBox(height: ThemeProvider.margin24), + if (widget.isSocialAuthEnabled) + const SizedBox(height: ThemeProvider.margin24), DerivSocialAuthPanel( isEnabled: !isReferralEnabled, onSocialAuthButtonPressed: widget.onSocialAuthButtonPressed, isVisible: widget.isSocialAuthEnabled, ), - const SizedBox(height: ThemeProvider.margin24), + if (widget.isSocialAuthEnabled) + const SizedBox(height: ThemeProvider.margin24), _buildFooterSection(), ], ), From f48c322cf1bc4a0d8a40d38e079fab33d468e79e Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:27:36 +0800 Subject: [PATCH 29/44] chore: add auth ui regex to deriv ui --- packages/deriv_ui/lib/utils/regex_helpers.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/deriv_ui/lib/utils/regex_helpers.dart b/packages/deriv_ui/lib/utils/regex_helpers.dart index 1a1d6e839..adfdfb75b 100644 --- a/packages/deriv_ui/lib/utils/regex_helpers.dart +++ b/packages/deriv_ui/lib/utils/regex_helpers.dart @@ -20,3 +20,10 @@ RegExp validPasswordLengthRegex = RegExp(r'^.{8,25}$'); /// Valid Password length for login. RegExp validLoginPasswordLengthRegex = RegExp(r'^.{6,25}$'); + +/// Check if [str] input contains only a-z letters and 0-9 numbers +bool hasOnlySmallLettersAndNumberInput(String str) => + RegExp('^[a-z0-9.]+\$').hasMatch(str); + +/// Check if [string] input contains only 0-9 numbers +bool hasOnlyNumberInput(String string) => RegExp('^[0-9]+\$').hasMatch(string); From ce8d202bc1636f29ca99475d645d99aead663e57 Mon Sep 17 00:00:00 2001 From: Ahrar <98078754+ahrar-deriv@users.noreply.github.com> Date: Tue, 21 Nov 2023 11:14:27 +0800 Subject: [PATCH 30/44] feat: [MOBC-608] auth ui setting page (#320) * feat: setting page * fix: depricated issues * fix: change package_info_plus to match packages * fix: future void callback * refactor: move regex_helper func to deriv_ui * fix: remove sahani repo from yaml files * fix: deriv_auth_ui pubspec * fix: remove appEnv related to deriv ez as deriv ez is removed from production we dont need to include it in this package anymore * fix: revert back the appEnv and change the comment --- packages/deriv_auth_ui/example/pubspec.yaml | 6 +- packages/deriv_auth_ui/lib/deriv_auth_ui.dart | 1 + .../lib/generated/intl/messages_en.dart | 11 + .../deriv_auth_ui/lib/generated/l10n.dart | 70 ++++++ packages/deriv_auth_ui/lib/l10n/intl_en.arb | 9 +- .../lib/src/core/helpers/endpoint_helper.dart | 36 +++ .../layouts/deriv_setting_layout.dart | 210 ++++++++++++++++++ .../models/deriv_password_policy_model.dart | 1 - packages/deriv_auth_ui/pubspec.yaml | 9 +- 9 files changed, 345 insertions(+), 8 deletions(-) create mode 100644 packages/deriv_auth_ui/lib/src/core/helpers/endpoint_helper.dart create mode 100644 packages/deriv_auth_ui/lib/src/features/setting_page/layouts/deriv_setting_layout.dart diff --git a/packages/deriv_auth_ui/example/pubspec.yaml b/packages/deriv_auth_ui/example/pubspec.yaml index d495c6853..312a84e02 100644 --- a/packages/deriv_auth_ui/example/pubspec.yaml +++ b/packages/deriv_auth_ui/example/pubspec.yaml @@ -12,13 +12,15 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.2 + deriv_auth_ui: path: ../ + deriv_auth: git: - url: git@github.com:sahani-deriv/flutter-deriv-packages.git + url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_auth - ref: auth-ui-update + ref: dev deriv_theme: git: diff --git a/packages/deriv_auth_ui/lib/deriv_auth_ui.dart b/packages/deriv_auth_ui/lib/deriv_auth_ui.dart index 20c4c1f3e..fc72bd304 100644 --- a/packages/deriv_auth_ui/lib/deriv_auth_ui.dart +++ b/packages/deriv_auth_ui/lib/deriv_auth_ui.dart @@ -6,6 +6,7 @@ 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/setting_page/layouts/deriv_setting_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'; diff --git a/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart b/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart index c45e05536..35d116dda 100644 --- a/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart +++ b/packages/deriv_auth_ui/lib/generated/intl/messages_en.dart @@ -110,6 +110,8 @@ class MessageLookup extends MessageLookupByLibrary { "informVerificationEmailSent": m1, "informYourPassHasBeenReset": MessageLookupByLibrary.simpleMessage( "Your password has been reset"), + "labelApplicationID": + MessageLookupByLibrary.simpleMessage("Application ID"), "labelCheckEmail": MessageLookupByLibrary.simpleMessage("Check your email"), "labelChooseCountry": @@ -122,6 +124,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Create a password"), "labelCreatePassword": MessageLookupByLibrary.simpleMessage("Create a password"), + "labelDeveloper": MessageLookupByLibrary.simpleMessage("Developer"), "labelDontHaveAnAccountYet": MessageLookupByLibrary.simpleMessage("Don’t have an account yet?"), "labelEmail": MessageLookupByLibrary.simpleMessage("Email"), @@ -135,6 +138,7 @@ class MessageLookup extends MessageLookupByLibrary { "The email address you entered had a mistake or typo (happens to the best of us)."), "labelEmailIssueWrongEmail": MessageLookupByLibrary.simpleMessage( "You accidentally gave us another email address (Usually a work or a personal one instead of the one you meant)."), + "labelEndpoint": MessageLookupByLibrary.simpleMessage("Endpoint"), "labelGotReferralCode": MessageLookupByLibrary.simpleMessage("Got a referral code?"), "labelHaveAccount": @@ -167,8 +171,15 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("2FA code"), "labelVerifyYourEmail": MessageLookupByLibrary.simpleMessage("Verify your email"), + "semanticApplicationID": + MessageLookupByLibrary.simpleMessage("Application ID"), + "semanticEndpoint": MessageLookupByLibrary.simpleMessage("Endpoint"), "warnCountryNotAvailable": MessageLookupByLibrary.simpleMessage( "Unfortunately, Deriv is not available in your country."), + "warnInvalidApplicationID": + MessageLookupByLibrary.simpleMessage("invalid application ID"), + "warnInvalidEndpoint": + MessageLookupByLibrary.simpleMessage("invalid endpoint"), "warnNotAvailableCountries": MessageLookupByLibrary.simpleMessage( "If you have any questions, contact us via "), "warnNotAvailableCountriesTitle": m2, diff --git a/packages/deriv_auth_ui/lib/generated/l10n.dart b/packages/deriv_auth_ui/lib/generated/l10n.dart index 9426718ce..55d19fff0 100644 --- a/packages/deriv_auth_ui/lib/generated/l10n.dart +++ b/packages/deriv_auth_ui/lib/generated/l10n.dart @@ -850,6 +850,76 @@ class DerivAuthUILocalization { args: [], ); } + + /// `Developer` + String get labelDeveloper { + return Intl.message( + 'Developer', + name: 'labelDeveloper', + desc: '', + args: [], + ); + } + + /// `Endpoint` + String get labelEndpoint { + return Intl.message( + 'Endpoint', + name: 'labelEndpoint', + desc: '', + args: [], + ); + } + + /// `Endpoint` + String get semanticEndpoint { + return Intl.message( + 'Endpoint', + name: 'semanticEndpoint', + desc: '', + args: [], + ); + } + + /// `invalid endpoint` + String get warnInvalidEndpoint { + return Intl.message( + 'invalid endpoint', + name: 'warnInvalidEndpoint', + desc: '', + args: [], + ); + } + + /// `Application ID` + String get labelApplicationID { + return Intl.message( + 'Application ID', + name: 'labelApplicationID', + desc: '', + args: [], + ); + } + + /// `Application ID` + String get semanticApplicationID { + return Intl.message( + 'Application ID', + name: 'semanticApplicationID', + desc: '', + args: [], + ); + } + + /// `invalid application ID` + String get warnInvalidApplicationID { + return Intl.message( + 'invalid application ID', + name: 'warnInvalidApplicationID', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate diff --git a/packages/deriv_auth_ui/lib/l10n/intl_en.arb b/packages/deriv_auth_ui/lib/l10n/intl_en.arb index bb220ebc7..fad40dd85 100644 --- a/packages/deriv_auth_ui/lib/l10n/intl_en.arb +++ b/packages/deriv_auth_ui/lib/l10n/intl_en.arb @@ -105,5 +105,12 @@ "informExpiredAccount": "Your account is expired", "labelCountryConsentBrazil": "I hereby confirm that my request for opening an account with Deriv to trade OTC products issued and offered exclusively outside Brazil was initiated by me. I fully understand that Deriv is not regulated by CVM and by approaching Deriv I intend to set up a relation with a foreign company.", "informConnectionError": "Connection error. Please try again later.", - "informSwitchAccountError": "Switch account error. Please try again later." + "informSwitchAccountError": "Switch account error. Please try again later.", + "labelDeveloper": "Developer", + "labelEndpoint": "Endpoint", + "semanticEndpoint": "Endpoint", + "warnInvalidEndpoint": "invalid endpoint", + "labelApplicationID": "Application ID", + "semanticApplicationID": "Application ID", + "warnInvalidApplicationID": "invalid application ID" } diff --git a/packages/deriv_auth_ui/lib/src/core/helpers/endpoint_helper.dart b/packages/deriv_auth_ui/lib/src/core/helpers/endpoint_helper.dart new file mode 100644 index 000000000..c7ad6b961 --- /dev/null +++ b/packages/deriv_auth_ui/lib/src/core/helpers/endpoint_helper.dart @@ -0,0 +1,36 @@ +/// Default auth endpoint. +const String defaultAuthEndpoint = 'oauth.deriv.com'; + +/// Default ws endpoint. +const String defaultEndpoint = 'blue.binaryws.com'; + +/// Default app id. +const String defaultAppId = '23789'; + +/// Parses an [endpoint] argument and generates a url that points to QA servers +/// if [endpoint] starts with `qa` or a url that points to production servers if +/// [endpoint] contains `derivws` or `binaryws` and [isAuthUrl] is `true`. +/// +/// [endpoint] argument is required. +/// [isAuthUrl] is optional and has a default value of `false`. +String? generateEndpointUrl({ + required String? endpoint, + bool isAuthUrl = false, +}) { + if (endpoint == null) { + return null; + } + + final RegExp qaRegExp = RegExp('^(qa[0-9]+)\$', caseSensitive: false); + final RegExp derivRegExp = + RegExp('(binary|deriv)ws\.(com|app)\$', caseSensitive: false); + + if (isAuthUrl && derivRegExp.hasMatch(endpoint)) { + // Since Deriv app is under Deriv.app, the oauth url should be always `oauth.deriv.com`. + return defaultAuthEndpoint; + } else if (qaRegExp.hasMatch(endpoint)) { + return '$endpoint.deriv.dev'; + } + + return endpoint; +} diff --git a/packages/deriv_auth_ui/lib/src/features/setting_page/layouts/deriv_setting_layout.dart b/packages/deriv_auth_ui/lib/src/features/setting_page/layouts/deriv_setting_layout.dart new file mode 100644 index 000000000..937970849 --- /dev/null +++ b/packages/deriv_auth_ui/lib/src/features/setting_page/layouts/deriv_setting_layout.dart @@ -0,0 +1,210 @@ +import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; +import 'package:deriv_auth_ui/src/core/helpers/endpoint_helper.dart'; +import 'package:deriv_theme/deriv_theme.dart'; +import 'package:deriv_ui/utils/regex_helpers.dart'; +import 'package:flutter/material.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +/// Login setting page +/// +/// This page is used to apply necessary QA configurations for login process +/// Two fields can be set in this page 'endpoint' and 'app_id' +/// The applied values stored for future usage +class DerivSettingLayout extends StatefulWidget { + /// Login Setting Page Constructor + const DerivSettingLayout({ + required this.updateFlavorConfigs, + required this.appLabel, + required this.saveValues, + this.endpoint = defaultEndpoint, + this.appId = defaultAppId, + this.devApp = 'com.deriv.app.dev', + this.stagingApp = 'com.deriv.app.staging', + this.getAppEnv, + this.setAppEnv, + Key? key, + }) : super(key: key); + + /// Update flavor configurations + final Future updateFlavorConfigs; + + /// Save values to shared preferences + final Function({required String endpoint, required String appId}) saveValues; + + /// End Point + final String endpoint; + + /// App Id + final String appId; + + /// Application label + final String appLabel; + + /// Dev flavor app. + final String devApp; + + /// Staging flavor app. + final String stagingApp; + + /// Gets environment variable + final Future? getAppEnv; + + /// Sets environment variable + final Future Function({required bool value})? setAppEnv; + + @override + _SettingsPageState createState() => _SettingsPageState(); +} + +class _SettingsPageState extends State { + final TextEditingController _endpointController = TextEditingController(); + final TextEditingController _appIdController = TextEditingController(); + + final GlobalKey _formKey = GlobalKey(); + late Future packageInfo; + + @override + void initState() { + super.initState(); + packageInfo = PackageInfo.fromPlatform(); + + _endpointController.text = widget.endpoint; + _appIdController.text = widget.appId; + } + + @override + Widget build(BuildContext context) => WillPopScope( + onWillPop: () async { + // Save Values to shared preferences + widget.saveValues( + endpoint: _endpointController.text.isNotEmpty + ? _endpointController.text + : defaultEndpoint, + appId: _appIdController.text.isNotEmpty + ? _appIdController.text + : defaultAppId, + ); + + // Update Flavor Configurations before dismissing the page + await widget.updateFlavorConfigs; + return true; + }, + child: Scaffold( + appBar: AppBar( + elevation: ThemeProvider.zeroMargin, + title: Text(widget.appLabel), + ), + body: Form( + key: _formKey, + child: ListView( + children: [ + _title, + const SizedBox(height: ThemeProvider.margin16), + _endpoint, + const SizedBox(height: ThemeProvider.margin16), + _appId, + const SizedBox(width: ThemeProvider.margin08), + _buildEnvironmentSwitcher, + ], + ), + ), + ), + ); + + Widget get _buildEnvironmentSwitcher => FutureBuilder( + future: packageInfo, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData && + (snapshot.data?.packageName == widget.devApp || + snapshot.data?.packageName == widget.stagingApp)) { + return Padding( + padding: + const EdgeInsets.symmetric(horizontal: ThemeProvider.margin16), + child: Row( + children: [ + const Text('Production environment'), + const SizedBox(width: ThemeProvider.margin08), + FutureBuilder( + future: widget.getAppEnv, + builder: + (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData && widget.setAppEnv != null) { + return Switch( + value: snapshot.data ?? false, + onChanged: (bool val) { + setState(() { + widget.setAppEnv!(value: val); + }); + }, + ); + } + return const SizedBox.shrink(); + }) + ], + ), + ); + } + return const SizedBox.shrink(); + }); + + Widget get _title => Padding( + padding: const EdgeInsets.only( + left: ThemeProvider.margin12, + top: ThemeProvider.margin24, + ), + child: Text( + context.localization.labelDeveloper, + style: context.theme.textStyle( + textStyle: TextStyles.body1Bold, + color: context.theme.colors.coral, + ), + ), + ); + + Widget get _endpoint => _buildTextInputField( + label: context.localization.labelEndpoint, + semantic: context.localization.semanticEndpoint, + controller: _endpointController, + validator: (String? value) => hasOnlySmallLettersAndNumberInput(value!) + ? null + : context.localization.warnInvalidEndpoint, + ); + + Widget get _appId => _buildTextInputField( + label: context.localization.labelApplicationID, + semantic: context.localization.semanticApplicationID, + controller: _appIdController, + validator: (String? value) => hasOnlyNumberInput(value!) + ? null + : context.localization.warnInvalidApplicationID, + ); + + Widget _buildTextInputField({ + required String label, + required String semantic, + required TextEditingController controller, + required FormFieldValidator validator, + }) => + Padding( + padding: const EdgeInsets.symmetric(horizontal: ThemeProvider.margin16), + child: Semantics( + label: semantic, + explicitChildNodes: true, + child: TextFormField( + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: context.theme.colors.lessProminent, + ), + ), + labelText: label, + labelStyle: TextStyle(color: context.theme.colors.lessProminent), + border: const OutlineInputBorder(), + ), + controller: controller, + onChanged: (_) => _formKey.currentState!.validate(), + validator: validator, + ), + ), + ); +} diff --git a/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_password_policy_model.dart b/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_password_policy_model.dart index cf5f0ef13..9edfec3ba 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_password_policy_model.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/models/deriv_password_policy_model.dart @@ -1,4 +1,3 @@ - /// Holds required information of a password policy. class DerivPasswordPolicyModel { /// Initializes [DerivPasswordPolicyModel]. diff --git a/packages/deriv_auth_ui/pubspec.yaml b/packages/deriv_auth_ui/pubspec.yaml index c15ade0f4..348fc5ce5 100644 --- a/packages/deriv_auth_ui/pubspec.yaml +++ b/packages/deriv_auth_ui/pubspec.yaml @@ -16,9 +16,9 @@ dependencies: deriv_auth: git: - url: git@github.com:sahani-deriv/flutter-deriv-packages.git + url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_auth - ref: auth-ui-update + ref: dev deriv_theme: git: @@ -28,15 +28,16 @@ dependencies: deriv_ui: git: - url: git@github.com:sahani-deriv/flutter-deriv-packages.git + url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: auth-ui-update + ref: dev flutter_svg: ^1.1.6 smooth_page_indicator: ^1.1.0 intl_utils: ^2.8.3 intl: ^0.18.0 equatable: ^2.0.5 + package_info_plus: ^3.0.3 dev_dependencies: flutter_test: From 829f78f96665c37350164ed609e9a06497c7a6fe Mon Sep 17 00:00:00 2001 From: Ahrar <98078754+ahrar-deriv@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:32:36 +0800 Subject: [PATCH 31/44] chore: merge master to dev (#334) * add update_checker package * add Readme * add comments * update documentation * apply suggestions from PR review * update deps * update flutter_bloc package * Revert "update flutter_bloc package" This reverts commit 7188a184363fafe0601f0fa904a3f42937e40978. * Update packages/update_checker/lib/update_info.dart Co-authored-by: abed-binary <61964755+abed-binary@users.noreply.github.com> * update bloc library to 6.0.1 * remove markseen * remove unnecessary statements * restore UpdateCheckMarkSeen * add UpdateCheckDismiss * push update_checker changes * Update web_socket_test.dart * chore: add pull request template with git rules * chore: pull request update git_rules.md --------- Co-authored-by: Suyash Mohan Co-authored-by: Suyash Mohan <33442325+suyash-binary@users.noreply.github.com> Co-authored-by: abed-binary <61964755+abed-binary@users.noreply.github.com> Co-authored-by: Morteza Tavanarad Co-authored-by: ramin-deriv Co-authored-by: ramin-deriv <55975218+ramin-deriv@users.noreply.github.com> Co-authored-by: WAQAS YOUNAS Co-authored-by: WAQAS YOUNAS <78855520+waqas-younas-deriv@users.noreply.github.com> --- .github/GIT_RULES.md | 172 ++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 55 ++++++ .../test/web_socket/web_socket_test.dart | 2 +- 3 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 .github/GIT_RULES.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/GIT_RULES.md b/.github/GIT_RULES.md new file mode 100644 index 000000000..2307dd09e --- /dev/null +++ b/.github/GIT_RULES.md @@ -0,0 +1,172 @@ +## Commit Rules: + +This commits rules is set to ensure all the developers follows a uniform way of writing commits so that it is easy to read the changes made and also automate versioning. +The commits are based on [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) which is a widely followed convention for commits. While writing a commit message to any changes made to code base, make sure it reflects the changes and follows the conventions i.e: + +``` +change_type(package_name): short description/subject line... + +Long Description... +``` + +Here, + +change_type means the type of changes like `feat`,`fix` and so on. + +package_name means which packages this changes is for. This is optional. + +short discription/subject line means about the changes + +Long Description are optional. Add it if its required to reflect the changes in more detail. + +For eg: + +Lets say the changes is about implementing a new feature which is about new social login mechanism in `deriv_auth` package. + +``` +feat(deriv_auth): add UI for sign in page + +- create reusable text field using global app theme +- add bloc for sign in logic implementation +- add google services for authentication +``` +### Breaking Changes Indicator +Breaking changes should be indicated by an `!` before the `:` in the subject line e.g. `feat(api)!: remove status endpoint` +* Is an **optional** part of the format + +More changes types: + +| Changes Types | Meaning | Description | +| ------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------- | +| feat | Features | A new feature | +| fix | Bug Fixes | A bug fix | +| chore | Chores | Other changes that don’t modify src or test files | +| docs | Docume­ntation | Docume­ntation only changes | +| revert | Reverts | Reverts a previous commit | +| refactor | Code Refact­oring | A code change that neither fixes a bug nor adds a feature | +| test | Tests | Adding missing tests or correcting existing tests | +| style | Styles | Changes that do not affect the meaning of the code (white­ -space, format­ting, missing semi-c­olons, etc) | +| perf | Perfor­mance Improv­ements | A code change that improves perfor­mance | +| build | Builds | Changes that affect the build system or external depend­encies (example scopes: gulp, broccoli, npm) | +| ci | Continuous Integr­ations | Changes to our CI config­uration files and scripts (example scopes: Travis, Circle, Browse­rStack, SauceLabs) | + +Optional: If you would like to pre populate your commit box with the commit template then you can do it by adding `.gitmessage` inside `.github` folder to commit template for that project with the following command.(Note: This won't work if you write commit via terminal.) + +`git config commit.template "YOUR_PROJECT_PATH/.github/.gitmessage"` + +### Examples +* ``` + feat: add email notifications on new direct messages + ``` +* ``` + feat(shopping cart): add the amazing button + ``` +* ``` + feat!: remove ticket list endpoint + + refers to JIRA-1337 + + BREAKING CHANGES: ticket enpoints no longer supports list all entites. + ``` +* ``` + fix(api): handle empty message in request body + ``` +* ``` + fix(api): fix wrong calculation of request body checksum + ``` +* ``` + fix: add missing parameter to service call + + The error occurred because of . + ``` +* ``` + perf: decrease memory footprint for determine uniqe visitors by using HyperLogLog + ``` +* ``` + build: update dependencies + ``` +* ``` + build(release): `bump version to 1.0.0 + ``` +* ``` + refactor: implement fibonacci number calculation as recursion + ``` +* ``` + style: remove empty line + ``` +## PR Rules: + +This Rules is set to create a uniform way of submitting Pull requests where all the necessary information for the changes are listed in the title, or description. There is a standard template for creating PR. When you are creating a PR to any repo always make sure: + +- you have titled it following conventional pattern and also included all the necessary information in it. + for eg: + Title: `feat(deriv_auth): [MOBC-299] Add ability to sign in with google` + + Here,
The title defines: + + - what type the changes/PR is about like feature, bug fixes, documentations, refactor.
+ - which package/app is this PR for.(optional)
+ - the clickup card id - short title that describes the changes
+ +- you have added detailed description that gives enough information about the PR. +- you have commits only relevant to your changes and not other's commit which shouldn't be there. + + Also, it is always better to squash commits that are only relevant to any particular PR. Such as changes requested through PR review. If these changes does not reflects any really meaning or value in the main project history then it is always better to squash such commits into one. It helps to maintain a clean commit history. + + **The template looks something like this:** + + + + **Clickup link:** + **Fixes issue:** # + + This PR contains the following changes: + + + + - [ ] ✨ New feature (non-breaking change which adds functionality) + - [ ] 🛠️ Bug fix (non-breaking change which fixes an issue) + - [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change) + - [ ] 🧹 Code refactor + - [ ] ✅ Build configuration change + - [ ] 📝 Documentation + - [ ] 🗑️ Chore + + ### Developers Note (Optional) + + + + ## Pre-launch Checklist (For PR creator) + + As a creator of this PR: + + + + - [ ] ✍️ I have included clickup id and package/app_name in the PR title. + - [ ] 👁️ I have gone through the code and removed any temporary changes (commented lines, prints, debug statements etc.). + - [ ] ⚒️ I have fixed any errors/warnings shown by the analyzer/linter. + - [ ] 📝 I have added documentation, comments and logging wherever required. + - [ ] 🧪 I have added necessary tests for these changes. + - [ ] 🔎 I have ensured all existing tests are passing. + + ## Reviewers + + + + ## Pre-launch Checklist (For Reviewers) + + As a reviewer I ensure that: + + - [ ] ✴️ This PR follows the standard PR template. + - [ ] ✴️ The information in this PR properly reflects the code changes. + - [ ] 🧪 All the necessary tests for this PR's are passing. + + ## Pre-launch Checklist (For QA) + + - [ ] 👌 It passes the acceptance criteria. + + ## Pre-launch Checklist (For Maintainer) + + - [ ] [MAINTAINER_NAME] I make sure this PR fulfills its purpose. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..e708d44da --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,55 @@ + + +**Clickup link:** +**Fixes issue:** # + +This PR contains the following changes: + + + +- [ ] ✨ New feature (non-breaking change which adds functionality) +- [ ] 🛠️ Bug fix (non-breaking change which fixes an issue) +- [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change) +- [ ] 🧹 Code refactor +- [ ] ✅ Build configuration change +- [ ] 📝 Documentation +- [ ] 🗑️ Chore + +### Developers Note (Optional) + + + +## Pre-launch Checklist (For PR creator) + +As a creator of this PR: + + + +- [ ] ✍️ I have included clickup id and package/app_name in the PR title. +- [ ] 👁️ I have gone through the code and removed any temporary changes (commented lines, prints, debug statements etc.). +- [ ] ⚒️ I have fixed any errors/warnings shown by the analyzer/linter. +- [ ] 📝 I have added documentation, comments and logging wherever required. +- [ ] 🧪 I have added necessary tests for these changes. +- [ ] 🔎 I have ensured all existing tests are passing. + +## Reviewers + + + +## Pre-launch Checklist (For Reviewers) + +As a reviewer I ensure that: + +- [ ] ✴️ This PR follows the standard PR template. +- [ ] 🪩 The information in this PR properly reflects the code changes. +- [ ] 🧪 All the necessary tests for this PR's are passing. + +## Pre-launch Checklist (For QA) + +- [ ] 👌 It passes the acceptance criteria. + +## Pre-launch Checklist (For Maintainer) + +- [ ] [MAINTAINER_NAME] I make sure this PR fulfills its purpose. diff --git a/packages/deriv_web_socket_client/test/web_socket/web_socket_test.dart b/packages/deriv_web_socket_client/test/web_socket/web_socket_test.dart index b19a1d15f..8ba66608a 100644 --- a/packages/deriv_web_socket_client/test/web_socket/web_socket_test.dart +++ b/packages/deriv_web_socket_client/test/web_socket/web_socket_test.dart @@ -262,7 +262,7 @@ void main() { ..send('ping') ..send('pong'); - await Future.delayed(Duration.zero); + await Future.delayed(const Duration(milliseconds: 500)); expect(messages, equals(['ping', 'pong'])); From 11f667c6b6053a712af880f7b9a57581739bac10 Mon Sep 17 00:00:00 2001 From: ramin-deriv <55975218+ramin-deriv@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:43:21 +0800 Subject: [PATCH 32/44] add invalidate and copyValuesFrom to AbstractEMAIndicator (#331) --- .../indicators/calculations/ema_indicator.dart | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/deriv_technical_analysis/lib/src/indicators/calculations/ema_indicator.dart b/packages/deriv_technical_analysis/lib/src/indicators/calculations/ema_indicator.dart index af0dfea00..26b3b1310 100644 --- a/packages/deriv_technical_analysis/lib/src/indicators/calculations/ema_indicator.dart +++ b/packages/deriv_technical_analysis/lib/src/indicators/calculations/ema_indicator.dart @@ -32,6 +32,24 @@ abstract class AbstractEMAIndicator prevValue, ); } + + @override + void copyValuesFrom(covariant AbstractEMAIndicator other) { + super.copyValuesFrom(other); + if (indicator is CachedIndicator) { + (indicator as CachedIndicator) + .copyValuesFrom(other.indicator as CachedIndicator); + } + } + + @override + void invalidate(int index) { + super.invalidate(index); + + if (indicator is CachedIndicator) { + (indicator as CachedIndicator).invalidate(index); + } + } } /// EMA indicator From 53ffa34282e2d58e30252927152de53a85855b1e Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:50:38 +0800 Subject: [PATCH 33/44] ci: automated versioning github actions and updated ref (#338) * deps: update dependencies update dependencies deriv_ui update dependencies part 1 update dependencies deriv_auth_ui * deps: push melos pubspec.lock * chore: publish packages chore(release): publish packages - analytics@1.0.0 - deriv_app_performance@0.0.1 - flutter_deriv_bloc_manager@0.0.1 - deriv_datadog@0.0.1 - deriv_dependency_injector@1.0.0 - deriv_env@0.0.1 - deriv_grouped_listview@0.0.1 - deriv_http_client@1.0.0 - deriv_lint@1.0.0 - deriv_rudderstack@1.1.0 - deriv_technical_analysis@0.0.1 - deriv_theme@2.0.0 - deriv_web_socket_client@1.0.0 - form_builder@1.0.0+1 - update_checker@1.1.0 chore(release): publish packages - deriv_auth@1.0.2 - deriv_banner@0.0.1 - deriv_date_range_picker@0.0.1 - deriv_expandable_bottom_sheet@0.0.1 - deriv_live_chat@0.0.1 - deriv_numpad@1.0.0+1 - deriv_store_launcher@0.0.1 - deriv_web_view@0.0.1 chore(release): publish packages - deriv_ui@0.0.1 chore(release): publish packages - deriv_auth_ui@0.0.1 * chore: clean changelog * chore: revert changelog changes * chore: change auth ui example app ref * ci: git actions for versioning --- .github/workflows/version.yaml | 55 +++ .gitignore | 3 +- packages/deriv_auth/pubspec.yaml | 2 +- packages/deriv_auth_ui/example/pubspec.yaml | 6 +- packages/deriv_auth_ui/pubspec.yaml | 9 +- packages/deriv_banner/example/pubspec.yaml | 2 +- packages/deriv_banner/pubspec.yaml | 2 +- packages/deriv_date_range_picker/pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- packages/deriv_lint/CHANGELOG.md | 2 + packages/deriv_live_chat/pubspec.yaml | 2 +- packages/deriv_numpad/pubspec.yaml | 2 +- .../deriv_store_launcher/example/pubspec.yaml | 2 +- packages/deriv_store_launcher/pubspec.yaml | 2 +- packages/deriv_ui/pubspec.yaml | 4 +- packages/deriv_web_view/pubspec.yaml | 2 +- pubspec.lock | 317 ++++++++++++++++++ pubspec.yaml | 2 +- 18 files changed, 395 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/version.yaml create mode 100644 packages/deriv_lint/CHANGELOG.md create mode 100644 pubspec.lock diff --git a/.github/workflows/version.yaml b/.github/workflows/version.yaml new file mode 100644 index 000000000..65114daf4 --- /dev/null +++ b/.github/workflows/version.yaml @@ -0,0 +1,55 @@ +name: version + +on: + pull_request: + types: + - closed + branches: + - dev + +permissions: + contents: write + pull-requests: write + +jobs: + version_and_tag: + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true && !contains(github.event.pull_request.title, 'chore(version)') + steps: + - name: Git Checkout + uses: actions/checkout@v4 + with: + ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} + fetch-depth: 0 + + - name: Setup Git User + uses: fregante/setup-git-user@v2 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: "3.10.2" + cache: true + + - name: Setup Melos + uses: bluefireteam/melos-action@v3 + with: + melos-version: "3.2.0" + run-bootstrap: false + + - name: Create git tag based on version + # only consider changes from the commit mentioned below + run: melos version --all --diff=6f208c9d5e7d4babfeb55dafcadec6ac76e88dea --yes + + - name: Push tag + run: git push --tags + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create Pull Request on updated changelog and pubspec file. + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.PAT }} + title: "chore(version): bump version and update changelog" + base: dev diff --git a/.gitignore b/.gitignore index 6f645b362..986f8f684 100644 --- a/.gitignore +++ b/.gitignore @@ -24,10 +24,11 @@ lib/basic_api/generated/*.json .flutter-plugins .packages .pub-cache/ -pubspec.lock .pub/ /build/ coverage/ +/packages/**/pubspec_overrides.yaml +/packages/**/pubspec.lock gradlew.bat diff --git a/packages/deriv_auth/pubspec.yaml b/packages/deriv_auth/pubspec.yaml index 764862ae5..0204a2199 100644 --- a/packages/deriv_auth/pubspec.yaml +++ b/packages/deriv_auth/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_http_client - ref: dev + ref: deriv_http_client-v1.0.0 bloc: ^8.1.1 crypto: ^3.0.2 diff --git a/packages/deriv_auth_ui/example/pubspec.yaml b/packages/deriv_auth_ui/example/pubspec.yaml index 312a84e02..155ac8da5 100644 --- a/packages/deriv_auth_ui/example/pubspec.yaml +++ b/packages/deriv_auth_ui/example/pubspec.yaml @@ -20,13 +20,13 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_auth - ref: dev - + ref: deriv_auth-v1.0.2 deriv_theme: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: dev + ref: deriv_theme-v2.0.0 + flutter_bloc: ^8.1.3 http: ^0.13.6 diff --git a/packages/deriv_auth_ui/pubspec.yaml b/packages/deriv_auth_ui/pubspec.yaml index 348fc5ce5..a31c22b02 100644 --- a/packages/deriv_auth_ui/pubspec.yaml +++ b/packages/deriv_auth_ui/pubspec.yaml @@ -18,20 +18,17 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_auth - ref: dev - + ref: deriv_auth-v1.0.2 deriv_theme: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: dev - + ref: deriv_theme-v2.0.0 deriv_ui: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: dev - + ref: deriv_ui-v0.0.1 flutter_svg: ^1.1.6 smooth_page_indicator: ^1.1.0 intl_utils: ^2.8.3 diff --git a/packages/deriv_banner/example/pubspec.yaml b/packages/deriv_banner/example/pubspec.yaml index 3a0e4b147..94c5b38b7 100644 --- a/packages/deriv_banner/example/pubspec.yaml +++ b/packages/deriv_banner/example/pubspec.yaml @@ -24,7 +24,7 @@ dev_dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_lint - ref: dev + ref: deriv_lint-v1.0.0 flutter: uses-material-design: true diff --git a/packages/deriv_banner/pubspec.yaml b/packages/deriv_banner/pubspec.yaml index 53b074ff2..dc7b16cb3 100644 --- a/packages/deriv_banner/pubspec.yaml +++ b/packages/deriv_banner/pubspec.yaml @@ -22,7 +22,7 @@ dev_dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_lint - ref: dev + ref: deriv_lint-v1.0.0 flutter: plugin: diff --git a/packages/deriv_date_range_picker/pubspec.yaml b/packages/deriv_date_range_picker/pubspec.yaml index d24ac1883..f579edc79 100644 --- a/packages/deriv_date_range_picker/pubspec.yaml +++ b/packages/deriv_date_range_picker/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: dev + ref: deriv_theme-v2.0.0 intl: ^0.18.0 diff --git a/packages/deriv_expandable_bottom_sheet/pubspec.yaml b/packages/deriv_expandable_bottom_sheet/pubspec.yaml index cfa726683..9d60f9bbf 100644 --- a/packages/deriv_expandable_bottom_sheet/pubspec.yaml +++ b/packages/deriv_expandable_bottom_sheet/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: dev + ref: deriv_theme-v2.0.0 dev_dependencies: flutter_test: diff --git a/packages/deriv_lint/CHANGELOG.md b/packages/deriv_lint/CHANGELOG.md new file mode 100644 index 000000000..139597f9c --- /dev/null +++ b/packages/deriv_lint/CHANGELOG.md @@ -0,0 +1,2 @@ + + diff --git a/packages/deriv_live_chat/pubspec.yaml b/packages/deriv_live_chat/pubspec.yaml index 0a33777ff..a68f13baf 100644 --- a/packages/deriv_live_chat/pubspec.yaml +++ b/packages/deriv_live_chat/pubspec.yaml @@ -20,7 +20,7 @@ dev_dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_lint - ref: dev + ref: deriv_lint-v1.0.0 flutter: plugin: diff --git a/packages/deriv_numpad/pubspec.yaml b/packages/deriv_numpad/pubspec.yaml index b16596119..5ad3ee2ce 100644 --- a/packages/deriv_numpad/pubspec.yaml +++ b/packages/deriv_numpad/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: dev + ref: deriv_theme-v2.0.0 flutter_svg: ^1.0.3 intl: ^0.17.0 diff --git a/packages/deriv_store_launcher/example/pubspec.yaml b/packages/deriv_store_launcher/example/pubspec.yaml index fecf5aef5..2b2f639da 100644 --- a/packages/deriv_store_launcher/example/pubspec.yaml +++ b/packages/deriv_store_launcher/example/pubspec.yaml @@ -24,7 +24,7 @@ dev_dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_lint - ref: dev + ref: deriv_lint-v1.0.0 flutter: uses-material-design: true diff --git a/packages/deriv_store_launcher/pubspec.yaml b/packages/deriv_store_launcher/pubspec.yaml index c59960d45..76666e1a2 100644 --- a/packages/deriv_store_launcher/pubspec.yaml +++ b/packages/deriv_store_launcher/pubspec.yaml @@ -22,7 +22,7 @@ dev_dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_lint - ref: dev + ref: deriv_lint-v1.0.0 flutter: plugin: diff --git a/packages/deriv_ui/pubspec.yaml b/packages/deriv_ui/pubspec.yaml index a689b2616..a8bcc0204 100644 --- a/packages/deriv_ui/pubspec.yaml +++ b/packages/deriv_ui/pubspec.yaml @@ -15,13 +15,13 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: dev + ref: deriv_theme-v2.0.0 deriv_web_view: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_web_view - ref: dev + ref: deriv_web_view-v0.0.1 flutter_svg: ^1.0.3 diff --git a/packages/deriv_web_view/pubspec.yaml b/packages/deriv_web_view/pubspec.yaml index 80178e50c..399fc7bf4 100644 --- a/packages/deriv_web_view/pubspec.yaml +++ b/packages/deriv_web_view/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_http_client - ref: dev + ref: deriv_http_client-v1.0.0 dev_dependencies: flutter_test: diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 000000000..5a8d1a5b1 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,317 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + ansi_styles: + dependency: transitive + description: + name: ansi_styles + sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a" + url: "https://pub.dev" + source: hosted + version: "0.3.2+1" + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" + cli_launcher: + dependency: transitive + description: + name: cli_launcher + sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + url: "https://pub.dev" + source: hosted + version: "0.4.0" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + conventional_commit: + dependency: transitive + description: + name: conventional_commit + sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2 + url: "https://pub.dev" + source: hosted + version: "0.6.0+1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + http: + dependency: transitive + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + melos: + dependency: "direct dev" + description: + name: melos + sha256: a45e54b72cc2444b46be9d32a590119b9ba8c4e87117f2743a73ec049542f2d3 + url: "https://pub.dev" + source: hosted + version: "3.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + mustache_template: + dependency: transitive + description: + name: mustache_template + sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c + url: "https://pub.dev" + source: hosted + version: "2.0.0" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + platform: + dependency: transitive + description: + name: platform + sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + prompts: + dependency: transitive + description: + name: prompts + sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pub_updater: + dependency: transitive + description: + name: pub_updater + sha256: b06600619c8c219065a548f8f7c192b3e080beff95488ed692780f48f69c0625 + url: "https://pub.dev" + source: hosted + version: "0.3.1" + pubspec: + dependency: transitive + description: + name: pubspec + sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e + url: "https://pub.dev" + source: hosted + version: "2.3.0" + quiver: + dependency: transitive + description: + name: quiver + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + url: "https://pub.dev" + source: hosted + version: "3.2.1" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + uri: + dependency: transitive + description: + name: uri + sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: "1579d4a0340a83cf9e4d580ea51a16329c916973bffd5bd4b45e911b25d46bfd" + url: "https://pub.dev" + source: hosted + version: "2.1.1" +sdks: + dart: ">=3.0.0 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9c29996ac..1e42b50bb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,4 +4,4 @@ environment: sdk: ">=2.18.0 <4.0.0" dev_dependencies: - melos: ^3.0.1 + melos: 3.2.0 From a7242c81b97fda70a622d7bbbb97fe997067117a Mon Sep 17 00:00:00 2001 From: ramin-deriv <55975218+ramin-deriv@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:30:56 +0800 Subject: [PATCH 34/44] refactor(deriv_env): make package independent of env file (#318) * get the env file content from outside * make Env class not being singleton * create EnvLoader class * fix the tests * bring back the removed test case * update the initialize doc * export classes need to be used by outside * export BaseEnv class as well * format deriv_env.dart * swape names for Env and EnvLoader to reduce the amount of change in the apps using this packagew * handle exception in bool type * remove duplicate lint rules * define common values as variable in env_test.dart * update README.md file * remove breaking change by making the laod method deprecated * Update CHANGELOG.md * udpate README.md file * refactor: Improve flexibility by removing dependency to env file - **Deprecation:** The `load` method is deprecated; it is recommended to use the new `initialize` method for improved flexibility. The `load` method will still function but may be removed in future releases. - Introduced a new method `initialize` to handle initialization without requiring an env file directly. - Improved package flexibility by removing the strict dependency on an env file for test environments. --- packages/deriv_env/README.md | 6 +- packages/deriv_env/analysis_options.yaml | 3 +- .../{base_env.dart => base_env_loader.dart} | 15 +- packages/deriv_env/lib/deriv_env.dart | 3 + packages/deriv_env/lib/env.dart | 138 +++++------------- packages/deriv_env/lib/env_loader.dart | 119 +++++++++++++++ packages/deriv_env/test/env_test.dart | 69 +++++---- 7 files changed, 215 insertions(+), 138 deletions(-) rename packages/deriv_env/lib/{base_env.dart => base_env_loader.dart} (55%) create mode 100644 packages/deriv_env/lib/deriv_env.dart create mode 100644 packages/deriv_env/lib/env_loader.dart diff --git a/packages/deriv_env/README.md b/packages/deriv_env/README.md index c03ca600a..43862781b 100644 --- a/packages/deriv_env/README.md +++ b/packages/deriv_env/README.md @@ -31,12 +31,12 @@ You can check if the environment variable is initialized using the `isInitialize bool isInitialized = Env().isInitialized; ``` -### Loading environment variables +### Initializing the Env -Before using any environment variables, you need to load them from a file. The `load` method loads the variables from a file with the specified filename (default is `.env`). +Before using any environment variables, you need to intialize the `Env` class and pass it an instnace of the EnvLoader and the path to the file. The `initialize` method loads the variables from a file with the specified filename (default is `.env`). ```dart -await Env().load(); +await Env().initialize(EnvLoader(filePath: '.env')); ``` The load method expects the file to contain key-value pairs separated by an equals sign (`=`) and each pair separated by a newline character (`\n`). Blank lines and comments (lines starting with a `#`) are ignored. diff --git a/packages/deriv_env/analysis_options.yaml b/packages/deriv_env/analysis_options.yaml index 91390bcb1..bf3a92f06 100644 --- a/packages/deriv_env/analysis_options.yaml +++ b/packages/deriv_env/analysis_options.yaml @@ -58,12 +58,11 @@ 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 diff --git a/packages/deriv_env/lib/base_env.dart b/packages/deriv_env/lib/base_env_loader.dart similarity index 55% rename from packages/deriv_env/lib/base_env.dart rename to packages/deriv_env/lib/base_env_loader.dart index 24700d332..59afabd53 100644 --- a/packages/deriv_env/lib/base_env.dart +++ b/packages/deriv_env/lib/base_env_loader.dart @@ -1,16 +1,19 @@ /// Base class for retrieve environment variables providers. -abstract class BaseEnv { +abstract class BaseEnvLoader { /// Returns `true` if [Env] is initialized, otherwise `false`. bool get isInitialized; /// Returns all environment variables as a [Map]. Map get entries; - /// Loads environment variables from a `.env` file. - /// - /// If [filename] is not provided, it will default to `.env`. - Future load([String filename = '.env']); + /// Loads environment variables. + Future loadEnvironment(); /// Retrieves an environment variable value by key. - T get(String key, {T? defaultValue}); + T get( + String key, { + T? defaultValue, + T Function(String value)? parser, + String decryptionKey = '', + }); } diff --git a/packages/deriv_env/lib/deriv_env.dart b/packages/deriv_env/lib/deriv_env.dart new file mode 100644 index 000000000..27ef791bf --- /dev/null +++ b/packages/deriv_env/lib/deriv_env.dart @@ -0,0 +1,3 @@ +export 'env.dart'; +export 'env_loader.dart'; +export 'base_env_loader.dart'; diff --git a/packages/deriv_env/lib/env.dart b/packages/deriv_env/lib/env.dart index 2d6af2877..3383f64a4 100644 --- a/packages/deriv_env/lib/env.dart +++ b/packages/deriv_env/lib/env.dart @@ -1,114 +1,56 @@ -import 'package:flutter/services.dart'; - -import 'base_env.dart'; -import 'cipher.dart'; - -/// [Env] class is a singleton class that provides access to environment variables. -class Env extends BaseEnv { - /// Returns the singleton instance of [Env]. - factory Env() => _instance; - - Env._(); +import 'base_env_loader.dart'; +import 'env_loader.dart'; + +/// This class is used to load environment variables from a .env file +class Env { + /// The singleton instance of [EnvLoader]. + factory Env() { + _instance ??= Env._internal(); + return _instance!; + } - static final Env _instance = Env._(); + Env._internal(); - bool _isInitialized = false; + static Env? _instance; - final Map _entries = {}; + /// The environment variables provider. + BaseEnvLoader? _env; - @override - bool get isInitialized => _isInitialized; + /// The instance of [BaseEnv]. + BaseEnvLoader? get env => _env; - @override - Map get entries { - _checkInitialization(); + /// Returns `true` if [Env] is initialized, otherwise `false`. + bool get isInitialized => _env?.isInitialized ?? false; - return _entries; + /// Initializes [EnvLoader] with an instance of [BaseEnv]. + /// Loads environment variables from a `.env` file. + /// + /// If [filename] is not provided, it will default to `.env`. + Future initialize(BaseEnvLoader env) async { + _env = env; + return _env!.loadEnvironment(); } - @override - Future load([String filename = '.env']) async { - _entries.clear(); - - final List fileEntries = await _getEntriesFromFile(filename); - - for (final String entry in fileEntries) { - final List items = entry.split('='); - - if (items.length > 1) { - _entries[items.first.trim()] = items.sublist(1).join('=').trim(); - } - } - - _isInitialized = true; + /// Loads environment variables from a `.env` file. + @Deprecated('Use initialize() method instead.') + Future load([String filePath = '.env']) async { + _env ??= EnvLoader(filePath: filePath); + return _env!.loadEnvironment(); } - @override + /// Retrieves an environment variable value by key. T get( String key, { T? defaultValue, T Function(String value)? parser, String decryptionKey = '', - }) { - _checkInitialization(); - - if (!_entries.containsKey(key)) { - if (defaultValue == null) { - throw Exception('$runtimeType does not contain a value for key: $key.'); - } - - return defaultValue; - } - - final String value = decryptionKey.isEmpty - ? _entries[key] - : Cipher().decrypt(message: _entries[key], key: decryptionKey); - - if (parser != null) { - return parser(value); - } - - switch (T) { - case int: - return int.tryParse(value) as T; - case double: - return double.tryParse(value) as T; - case bool: - return (value.toLowerCase() == 'true') as T; - - default: - return value as T; - } - } - - Future> _getEntriesFromFile(String filename) async { - final String envFileContent = await rootBundle.loadString(filename); - - if (envFileContent.isEmpty) { - throw Exception('$runtimeType: $filename is empty.'); - } - - final List entries = []; - final List content = envFileContent.split('\n'); - - for (final String line in content) { - if (line.isEmpty || line.startsWith('#')) { - continue; - } - - entries.add(line); - } - - return entries; - } - - void _checkInitialization() { - if (_isInitialized) { - return; - } - - throw Exception( - '$runtimeType is not initialized, call load() method first.', - ); - } + }) => + isInitialized + ? _env!.get( + key, + defaultValue: defaultValue, + parser: parser, + decryptionKey: decryptionKey, + ) + : throw Exception('EnvLoader is not initialized.'); } diff --git a/packages/deriv_env/lib/env_loader.dart b/packages/deriv_env/lib/env_loader.dart new file mode 100644 index 000000000..c3a8d9412 --- /dev/null +++ b/packages/deriv_env/lib/env_loader.dart @@ -0,0 +1,119 @@ +import 'package:flutter/services.dart'; + +import 'base_env_loader.dart'; +import 'cipher.dart'; + +/// [Env] class is a singleton class that provides access to environment variables. +class EnvLoader extends BaseEnvLoader { + /// Creates a new instance of [EnvLoader]. + EnvLoader({required this.filePath}); + + /// The path to the file. + final String filePath; + + bool _isInitialized = false; + + final Map _entries = {}; + + @override + bool get isInitialized => _isInitialized; + + @override + Map get entries { + _checkInitialization(); + + return _entries; + } + + @override + Future loadEnvironment() async { + _entries.clear(); + + final List fileEntries = await _getEntriesFromFile(filePath); + + for (final String entry in fileEntries) { + final List items = entry.split('='); + + if (items.length > 1) { + _entries[items.first.trim()] = items.sublist(1).join('=').trim(); + } + } + + _isInitialized = true; + } + + @override + T get( + String key, { + T? defaultValue, + T Function(String value)? parser, + String decryptionKey = '', + }) { + _checkInitialization(); + + if (!_entries.containsKey(key)) { + if (defaultValue == null) { + throw Exception('$runtimeType does not contain a value for key: $key.'); + } + + return defaultValue; + } + + final String value = decryptionKey.isEmpty + ? _entries[key] + : Cipher().decrypt(message: _entries[key], key: decryptionKey); + + if (parser != null) { + return parser(value); + } + + switch (T) { + case int: + return int.tryParse(value) as T; + case double: + return double.tryParse(value) as T; + case bool: + if (value.toLowerCase() == 'true') { + return true as T; + } else if (value.toLowerCase() == 'false') { + return false as T; + } else { + throw FormatException('Invalid boolean value: $value'); + } + + default: + return value as T; + } + } + + Future> _getEntriesFromFile(String filename) async { + final String envFileContent = await rootBundle.loadString(filename); + + if (envFileContent.isEmpty) { + throw Exception('$runtimeType: $filename is empty.'); + } + + final List entries = []; + final List content = envFileContent.split('\n'); + + for (final String line in content) { + if (line.isEmpty || line.startsWith('#')) { + continue; + } + + entries.add(line); + } + + return entries; + } + + void _checkInitialization() { + if (_isInitialized) { + return; + } + + throw Exception( + '$runtimeType is not initialized, call load() method first.', + ); + } +} diff --git a/packages/deriv_env/test/env_test.dart b/packages/deriv_env/test/env_test.dart index 1221a7108..59b338fca 100644 --- a/packages/deriv_env/test/env_test.dart +++ b/packages/deriv_env/test/env_test.dart @@ -1,3 +1,4 @@ +import 'package:deriv_env/env_loader.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:deriv_env/env.dart'; @@ -5,6 +6,14 @@ import 'package:deriv_env/env.dart'; void main() { setUpAll(() => TestWidgetsFlutterBinding.ensureInitialized()); + const String stringKey = 'STRING_VAR'; + const String intKey = 'INT_VAR'; + const String doubleKey = 'DOUBLE_VAR'; + const String boolKey = 'BOOL_VAR'; + const String varWithEqualsKey = 'VAR_WITH_EQUALS'; + const String varWithHashKey = 'VAR_WITH_HASH'; + const String encryptedKey = 'ENCRYPTED_VAR'; + group('env class test =>', () { test('get() method should throw exception if env is not initialized.', () async { @@ -14,27 +23,27 @@ void main() { test('load() method should populate env map.', () async { expect(Env().isInitialized, isFalse); - await Env().load('test/.env.test'); + await Env().initialize(EnvLoader(filePath: 'test/.env.test')); expect(Env().isInitialized, isTrue); - expect(Env().entries.length, 7); + expect(Env().env!.entries.length, 7); - expect(Env().entries['STRING_VAR'], 'hello world'); - expect(Env().entries['INT_VAR'], '123'); - expect(Env().entries['DOUBLE_VAR'], '3.14'); - expect(Env().entries['BOOL_VAR'], 'true'); - expect(Env().entries['VAR_WITH_EQUALS'], 'hello=world'); - expect(Env().entries['VAR_WITH_HASH'], 'hello#world'); + expect(Env().env!.entries[stringKey], 'hello world'); + expect(Env().env!.entries[intKey], '123'); + expect(Env().env!.entries[doubleKey], '3.14'); + expect(Env().env!.entries[boolKey], 'true'); + expect(Env().env!.entries[varWithEqualsKey], 'hello=world'); + expect(Env().env!.entries[varWithHashKey], 'hello#world'); expect( - Env().entries['ENCRYPTED_VAR'], + Env().env!.entries[encryptedKey], 'dVyH3QjdHYcjcS2TQ1XenmDVvf5ViN8ZpSVEcjfFhsk=', ); }); test('get() method should return default value if key is not found.', () async { - await Env().load('test/.env.test'); + await Env().initialize(EnvLoader(filePath: 'test/.env.test')); expect( Env().get('INVALID_KEY', defaultValue: 'default'), @@ -43,31 +52,31 @@ void main() { }); test('get() method should parse value as int.', () async { - await Env().load('test/.env.test'); + await Env().initialize(EnvLoader(filePath: 'test/.env.test')); - expect(Env().get('INT_VAR'), 123); + expect(Env().get(intKey), 123); }); test('get() method should parse value as double.', () async { - await Env().load('test/.env.test'); + await Env().initialize(EnvLoader(filePath: 'test/.env.test')); - expect(Env().get('DOUBLE_VAR'), 3.14); + expect(Env().get(doubleKey), 3.14); }); test('get() method should parse value as bool.', () async { - await Env().load('test/.env.test'); + await Env().initialize(EnvLoader(filePath: 'test/.env.test')); - expect(Env().get('BOOL_VAR'), isTrue); + expect(Env().get(boolKey), isTrue); }); test( 'get() method should parse value with a parser factory if it is provided.', () async { - await Env().load('test/.env.test'); + await Env().initialize(EnvLoader(filePath: 'test/.env.test')); expect( Env().get( - 'STRING_VAR', + stringKey, parser: (String value) => value.toUpperCase(), ), 'HELLO WORLD', @@ -75,7 +84,7 @@ void main() { expect( Env().get( - 'INT_VAR', + intKey, parser: (String value) => int.parse(value) * 2, ), 246, @@ -83,7 +92,7 @@ void main() { expect( Env().get( - 'DOUBLE_VAR', + doubleKey, parser: (String value) => double.parse(value) * 2, ), 6.28, @@ -91,7 +100,7 @@ void main() { expect( Env().get( - 'DOUBLE_VAR', + doubleKey, parser: (String value) => double.parse(value) > 3.14, ), false, @@ -100,18 +109,18 @@ void main() { test('check handling variables with special characters like `#` and `=`.', () async { - await Env().load('test/.env.test'); + await Env().initialize(EnvLoader(filePath: 'test/.env.test')); - expect(Env().entries['VAR_WITH_EQUALS'], 'hello=world'); - expect(Env().entries['VAR_WITH_HASH'], 'hello#world'); + expect(Env().env!.entries[varWithEqualsKey], 'hello=world'); + expect(Env().env!.entries[varWithHashKey], 'hello#world'); }); test('handle encrypted variable.', () async { - await Env().load('test/.env.test'); + await Env().initialize(EnvLoader(filePath: 'test/.env.test')); expect( Env().get( - 'ENCRYPTED_VAR', + encryptedKey, decryptionKey: 'TbKjMndW1L8vczgGQfPo2IyUxh6XAEay', ), 'ecnrypted message', @@ -119,7 +128,9 @@ void main() { }); test('throws an exception if file is empty.', () async { - expect(() => Env().load('test/.env.empty.test'), throwsException); + expect( + () => Env().initialize(EnvLoader(filePath: 'test/.env.empty.test')), + throwsException); }); test('throws an exception if env is not initialized.', () async { @@ -127,7 +138,7 @@ void main() { }); test('throws an exception if key is not found.', () async { - await Env().load('test/.env.test'); + await Env().initialize(EnvLoader(filePath: 'test/.env.test')); expect(() => Env().get('INVALID_KEY'), throwsException); }); @@ -135,7 +146,7 @@ void main() { test( 'does not throw an exception if key is not found and a default value is provided.', () async { - await Env().load('test/.env.test'); + await Env().initialize(EnvLoader(filePath: 'test/.env.test')); expect(() => Env().get('INVALID_KEY', defaultValue: 42), returnsNormally); }); From 2cdb3230443e432eb35a666b60e7cdcf455cf3e1 Mon Sep 17 00:00:00 2001 From: Ahrar <98078754+ahrar-deriv@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:57:39 +0800 Subject: [PATCH 35/44] chore: [MOBC-632] remove commit hash ci (#340) --- .github/workflows/version.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/version.yaml b/.github/workflows/version.yaml index 65114daf4..c93910aab 100644 --- a/.github/workflows/version.yaml +++ b/.github/workflows/version.yaml @@ -40,7 +40,7 @@ jobs: - name: Create git tag based on version # only consider changes from the commit mentioned below - run: melos version --all --diff=6f208c9d5e7d4babfeb55dafcadec6ac76e88dea --yes + run: melos version --all --yes - name: Push tag run: git push --tags From 0758010e9fa4dc609f3f394dd0b032558d7c3949 Mon Sep 17 00:00:00 2001 From: mobile-apps-deriv <134251399+mobile-apps-deriv@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:06:09 +0800 Subject: [PATCH 36/44] chore(version): bump version and update changelog (#342) - deriv_env@0.0.1+1 Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 25 +++++++++++++++++++++++++ packages/deriv_env/CHANGELOG.md | 4 ++++ packages/deriv_env/pubspec.yaml | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..71c80c6a7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,25 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## 2023-11-27 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_env` - `v0.0.1+1`](#deriv_env---v0011) + +--- + +#### `deriv_env` - `v0.0.1+1` + + - **REFACTOR**(deriv_env): make package independent of env file ([#318](https://github.com/regentmarkets/flutter-deriv-packages/issues/318)). ([a7242c81](https://github.com/regentmarkets/flutter-deriv-packages/commit/a7242c81b97fda70a622d7bbbb97fe997067117a)) + diff --git a/packages/deriv_env/CHANGELOG.md b/packages/deriv_env/CHANGELOG.md index 761894f12..ecd5433ba 100644 --- a/packages/deriv_env/CHANGELOG.md +++ b/packages/deriv_env/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+1 + + - **REFACTOR**(deriv_env): make package independent of env file ([#318](https://github.com/regentmarkets/flutter-deriv-packages/issues/318)). ([a7242c81](https://github.com/regentmarkets/flutter-deriv-packages/commit/a7242c81b97fda70a622d7bbbb97fe997067117a)) + ## 0.0.1 - initial release. diff --git a/packages/deriv_env/pubspec.yaml b/packages/deriv_env/pubspec.yaml index ef12510bc..b12aa29ec 100644 --- a/packages/deriv_env/pubspec.yaml +++ b/packages/deriv_env/pubspec.yaml @@ -1,7 +1,7 @@ name: deriv_env description: A package to load and store environment variables. -version: 0.0.1 +version: 0.0.1+1 environment: sdk: ">=3.0.0 <4.0.0" From c8e27e0c6771616b308ec003a259b98b9bd5774e Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:00:00 +0800 Subject: [PATCH 37/44] chore(deriv_auth_ui): remove unecessary deps from example (#343) --- packages/deriv_auth_ui/example/pubspec.yaml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/deriv_auth_ui/example/pubspec.yaml b/packages/deriv_auth_ui/example/pubspec.yaml index 155ac8da5..44b8282a0 100644 --- a/packages/deriv_auth_ui/example/pubspec.yaml +++ b/packages/deriv_auth_ui/example/pubspec.yaml @@ -12,21 +12,8 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.2 - deriv_auth_ui: path: ../ - - deriv_auth: - git: - url: git@github.com:regentmarkets/flutter-deriv-packages.git - path: packages/deriv_auth - ref: deriv_auth-v1.0.2 - deriv_theme: - git: - url: git@github.com:regentmarkets/flutter-deriv-packages.git - path: packages/deriv_theme - ref: deriv_theme-v2.0.0 - flutter_bloc: ^8.1.3 http: ^0.13.6 From bf6258206b4bb4cbdd1ef6744e07e2adb8d0d5ee Mon Sep 17 00:00:00 2001 From: ayaan-deriv <142789997+ayaan-deriv@users.noreply.github.com> Date: Tue, 28 Nov 2023 10:49:57 +0400 Subject: [PATCH 38/44] feat: Ayaan/Added margins 112 and 164 (#319) * Add dimens 112 and 164 * Update dimens.dart --- packages/deriv_theme/lib/src/dimens.dart | 6 ++++++ packages/deriv_theme/lib/theme_provider.dart | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/packages/deriv_theme/lib/src/dimens.dart b/packages/deriv_theme/lib/src/dimens.dart index b13f4812d..51ae29b44 100644 --- a/packages/deriv_theme/lib/src/dimens.dart +++ b/packages/deriv_theme/lib/src/dimens.dart @@ -73,12 +73,18 @@ class Dimens { /// 96 pixels margin. static const double margin96 = 96; + /// 112 pixels margin. + static const double margin112 = 112; + /// 128 pixels margin. static const double margin128 = 128; /// 144 pixels margin. static const double margin144 = 144; + /// 164 pixels margin. + static const double margin164 = 164; + /// 176 pixels margin. static const double margin176 = 176; diff --git a/packages/deriv_theme/lib/theme_provider.dart b/packages/deriv_theme/lib/theme_provider.dart index b9bc21ec5..2fc100c52 100644 --- a/packages/deriv_theme/lib/theme_provider.dart +++ b/packages/deriv_theme/lib/theme_provider.dart @@ -66,10 +66,14 @@ class ThemeProvider { static const double margin96 = Dimens.margin96; + static const double margin112 = Dimens.margin112; + static const double margin128 = Dimens.margin128; static const double margin144 = Dimens.margin144; + static const double margin164 = Dimens.margin164; + static const double margin176 = Dimens.margin176; static const double margin192 = Dimens.margin192; From 2eaddf506f9026cf9f14a3e018a0cefc7d09f941 Mon Sep 17 00:00:00 2001 From: mobile-apps-deriv <134251399+mobile-apps-deriv@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:49:28 +0800 Subject: [PATCH 39/44] chore(release): publish packages (#344) - deriv_theme@2.1.0 - deriv_date_range_picker@0.0.1+1 - deriv_ui@0.0.1+1 - deriv_auth_ui@0.0.1+1 - deriv_expandable_bottom_sheet@0.0.1+1 - deriv_numpad@1.0.1 Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 36 +++++++++++++++++++ packages/deriv_auth_ui/CHANGELOG.md | 4 +++ packages/deriv_auth_ui/pubspec.yaml | 6 ++-- packages/deriv_date_range_picker/CHANGELOG.md | 4 +++ packages/deriv_date_range_picker/pubspec.yaml | 4 +-- .../CHANGELOG.md | 4 +++ .../pubspec.yaml | 4 +-- packages/deriv_numpad/CHANGELOG.md | 4 +++ packages/deriv_numpad/pubspec.yaml | 4 +-- packages/deriv_theme/CHANGELOG.md | 4 +++ packages/deriv_theme/pubspec.yaml | 2 +- packages/deriv_ui/CHANGELOG.md | 4 +++ packages/deriv_ui/pubspec.yaml | 4 +-- 13 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 packages/deriv_numpad/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 71c80c6a7..5e1ca4fc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,42 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2023-11-28 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_theme` - `v2.1.0`](#deriv_theme---v210) + - [`deriv_date_range_picker` - `v0.0.1+1`](#deriv_date_range_picker---v0011) + - [`deriv_ui` - `v0.0.1+1`](#deriv_ui---v0011) + - [`deriv_auth_ui` - `v0.0.1+1`](#deriv_auth_ui---v0011) + - [`deriv_expandable_bottom_sheet` - `v0.0.1+1`](#deriv_expandable_bottom_sheet---v0011) + - [`deriv_numpad` - `v1.0.1`](#deriv_numpad---v101) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `deriv_date_range_picker` - `v0.0.1+1` + - `deriv_ui` - `v0.0.1+1` + - `deriv_auth_ui` - `v0.0.1+1` + - `deriv_expandable_bottom_sheet` - `v0.0.1+1` + - `deriv_numpad` - `v1.0.1` + +--- + +#### `deriv_theme` - `v2.1.0` + + - **FEAT**: Ayaan/Added margins 112 and 164 ([#319](https://github.com/regentmarkets/flutter-deriv-packages/issues/319)). ([bf625820](https://github.com/regentmarkets/flutter-deriv-packages/commit/bf6258206b4bb4cbdd1ef6744e07e2adb8d0d5ee)) + + ## 2023-11-27 ### Changes diff --git a/packages/deriv_auth_ui/CHANGELOG.md b/packages/deriv_auth_ui/CHANGELOG.md index 5a8e15d2f..cf1939e79 100644 --- a/packages/deriv_auth_ui/CHANGELOG.md +++ b/packages/deriv_auth_ui/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+1 + + - Update a dependency to the latest release. + ## 0.0.1 * Init project diff --git a/packages/deriv_auth_ui/pubspec.yaml b/packages/deriv_auth_ui/pubspec.yaml index a31c22b02..7b54f8635 100644 --- a/packages/deriv_auth_ui/pubspec.yaml +++ b/packages/deriv_auth_ui/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_auth_ui description: A flutter package for deriv auth UI flows. -version: 0.0.1 +version: 0.0.1+1 environment: sdk: ">=3.0.0 <4.0.0" @@ -23,12 +23,12 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: deriv_theme-v2.0.0 + ref: deriv_theme-v2.1.0 deriv_ui: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: deriv_ui-v0.0.1 + ref: deriv_ui-v0.0.1+1 flutter_svg: ^1.1.6 smooth_page_indicator: ^1.1.0 intl_utils: ^2.8.3 diff --git a/packages/deriv_date_range_picker/CHANGELOG.md b/packages/deriv_date_range_picker/CHANGELOG.md index 41cc7d819..b7f2af439 100644 --- a/packages/deriv_date_range_picker/CHANGELOG.md +++ b/packages/deriv_date_range_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+1 + + - Update a dependency to the latest release. + ## 0.0.1 * TODO: Describe initial release. diff --git a/packages/deriv_date_range_picker/pubspec.yaml b/packages/deriv_date_range_picker/pubspec.yaml index f579edc79..a188da8a5 100644 --- a/packages/deriv_date_range_picker/pubspec.yaml +++ b/packages/deriv_date_range_picker/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_date_range_picker description: A new Flutter package project. -version: 0.0.1 +version: 0.0.1+1 publish_to: "none" # Remove this line if you wish to publish to pub.dev environment: @@ -17,7 +17,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: deriv_theme-v2.0.0 + ref: deriv_theme-v2.1.0 intl: ^0.18.0 diff --git a/packages/deriv_expandable_bottom_sheet/CHANGELOG.md b/packages/deriv_expandable_bottom_sheet/CHANGELOG.md index 41cc7d819..b7f2af439 100644 --- a/packages/deriv_expandable_bottom_sheet/CHANGELOG.md +++ b/packages/deriv_expandable_bottom_sheet/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+1 + + - Update a dependency to the latest release. + ## 0.0.1 * TODO: Describe initial release. diff --git a/packages/deriv_expandable_bottom_sheet/pubspec.yaml b/packages/deriv_expandable_bottom_sheet/pubspec.yaml index 9d60f9bbf..2b33fe075 100644 --- a/packages/deriv_expandable_bottom_sheet/pubspec.yaml +++ b/packages/deriv_expandable_bottom_sheet/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_expandable_bottom_sheet description: A new Flutter package project. -version: 0.0.1 +version: 0.0.1+1 publish_to: none environment: @@ -15,7 +15,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: deriv_theme-v2.0.0 + ref: deriv_theme-v2.1.0 dev_dependencies: flutter_test: diff --git a/packages/deriv_numpad/CHANGELOG.md b/packages/deriv_numpad/CHANGELOG.md new file mode 100644 index 000000000..e9bf0b29c --- /dev/null +++ b/packages/deriv_numpad/CHANGELOG.md @@ -0,0 +1,4 @@ +## 1.0.1 + + - Update a dependency to the latest release. + diff --git a/packages/deriv_numpad/pubspec.yaml b/packages/deriv_numpad/pubspec.yaml index 5ad3ee2ce..cdc86e058 100644 --- a/packages/deriv_numpad/pubspec.yaml +++ b/packages/deriv_numpad/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_numpad description: A new Flutter project. -version: 1.0.0+1 +version: 1.0.1 publish_to: "none" environment: @@ -13,7 +13,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: deriv_theme-v2.0.0 + ref: deriv_theme-v2.1.0 flutter_svg: ^1.0.3 intl: ^0.17.0 diff --git a/packages/deriv_theme/CHANGELOG.md b/packages/deriv_theme/CHANGELOG.md index 2087a73ac..b92d4999e 100644 --- a/packages/deriv_theme/CHANGELOG.md +++ b/packages/deriv_theme/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + + - **FEAT**: Ayaan/Added margins 112 and 164 ([#319](https://github.com/regentmarkets/flutter-deriv-packages/issues/319)). ([bf625820](https://github.com/regentmarkets/flutter-deriv-packages/commit/bf6258206b4bb4cbdd1ef6744e07e2adb8d0d5ee)) + ## [2.0.0] - Migrated to null safety diff --git a/packages/deriv_theme/pubspec.yaml b/packages/deriv_theme/pubspec.yaml index a8a2db7e4..79813bdcf 100644 --- a/packages/deriv_theme/pubspec.yaml +++ b/packages/deriv_theme/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_theme description: A flutter package that contains the theme used by deriv products -version: 2.0.0 +version: 2.1.0 homepage: https://deriv.com/ publish_to: "none" diff --git a/packages/deriv_ui/CHANGELOG.md b/packages/deriv_ui/CHANGELOG.md index 41cc7d819..b7f2af439 100644 --- a/packages/deriv_ui/CHANGELOG.md +++ b/packages/deriv_ui/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+1 + + - Update a dependency to the latest release. + ## 0.0.1 * TODO: Describe initial release. diff --git a/packages/deriv_ui/pubspec.yaml b/packages/deriv_ui/pubspec.yaml index a8bcc0204..a804b66a8 100644 --- a/packages/deriv_ui/pubspec.yaml +++ b/packages/deriv_ui/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_ui description: A new Flutter package project. -version: 0.0.1 +version: 0.0.1+1 publish_to: none environment: @@ -15,7 +15,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: deriv_theme-v2.0.0 + ref: deriv_theme-v2.1.0 deriv_web_view: git: From 36a0c1faa0b47d4f735a79daf67c9e2c0089938e Mon Sep 17 00:00:00 2001 From: ernest-deriv <120568427+ernest-deriv@users.noreply.github.com> Date: Wed, 29 Nov 2023 06:54:37 +0400 Subject: [PATCH 40/44] refactor(deriv_auth_ui): [MOBC-629] Adding semantics to UI components (#321) * chore(deriv_auth_ui):Adding semantics to UI components * chore(deriv_auth_ui):Adding semantics to UI components, fixes based on review comments * chore(deriv_auth_ui):Adding explicitChildNodes: true to Semantics Widgets --- .../lib/src/core/helpers/semantic_labels.dart | 41 +++++++ .../layouts/deriv_get_started_layout.dart | 41 ++++--- .../login/layouts/deriv_login_layout.dart | 3 + .../layouts/deriv_reset_pass_layout.dart | 50 ++++---- .../signup/layouts/deriv_signup_layout.dart | 43 ++++--- .../presentation/widgets/base_text_field.dart | 113 ++++++++++-------- 6 files changed, 182 insertions(+), 109 deletions(-) create mode 100644 packages/deriv_auth_ui/lib/src/core/helpers/semantic_labels.dart diff --git a/packages/deriv_auth_ui/lib/src/core/helpers/semantic_labels.dart b/packages/deriv_auth_ui/lib/src/core/helpers/semantic_labels.dart new file mode 100644 index 000000000..b0f7e49df --- /dev/null +++ b/packages/deriv_auth_ui/lib/src/core/helpers/semantic_labels.dart @@ -0,0 +1,41 @@ +/// A class that contains semantic labels for various UI elements in the auth UI flow. +class SemanticsLabels { + // Signup Page + + /// A semantic label for the email field on the signup page. + static String signupEmailFieldSemantic = 'signup_email_field_semantic'; + + /// A semantic label for the signup button on the signup page. + static String signupButtonSemantic = 'signup_button_semantic'; + + /// A semantic label for the referral text field on the signup page. + static String signupReferralTextFieldSemantic = + 'signup_referral_field_semantic'; + + // Login Page + + /// A semantic label for the email field on the login page. + static String loginEmailFieldSemantic = 'login_email_field_semantic'; + + /// A semantic label for the password field on the login page. + static String loginPasswordFieldSemantic = 'login_password_field_semantic'; + + // Reset Password Page + + /// A semantic label for the email field on the reset password page. + static String resetPasswordEmailFieldSemantic = + 'reset_password_email_field_semantic'; + + /// A semantic label for the reset password button on the reset password page. + static String resetPasswordButtonSemantic = 'reset_password_button_semantic'; + + // Starter Page + + /// A semantic label for the signup button on the starter page. + static String starterPageSignupButtonSemantic = + 'starter_page_signup_button_semantic'; + + /// A semantic label for the login button on the starter page. + static String starterPageLoginButtonSemantic = + 'starter_page_login_button_semantic'; +} diff --git a/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart b/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart index c074202e0..de6ce0670 100644 --- a/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/get_started/layouts/deriv_get_started_layout.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:math' as math; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; +import 'package:deriv_auth_ui/src/core/helpers/semantic_labels.dart'; import 'package:deriv_auth_ui/src/features/get_started/models/deriv_get_started_slide_model.dart'; import 'package:deriv_theme/deriv_theme.dart'; import 'package:deriv_ui/deriv_ui.dart'; @@ -139,26 +140,34 @@ class _DerivGetStartedLayoutState extends State { Widget _buildButtons() => Column( mainAxisSize: MainAxisSize.min, children: [ - PrimaryButton( - onPressed: widget.onSignupTapped, - child: Center( - child: Text( - context.localization.actionGetAFreeAccount, - style: context.theme.textStyle( - textStyle: TextStyles.body2, - color: context.theme.colors.prominent, + Semantics( + explicitChildNodes: true, + label: SemanticsLabels.starterPageSignupButtonSemantic, + child: PrimaryButton( + onPressed: widget.onSignupTapped, + child: Center( + child: Text( + context.localization.actionGetAFreeAccount, + style: context.theme.textStyle( + textStyle: TextStyles.body2, + color: context.theme.colors.prominent, + ), ), ), ), ), - SecondaryButton( - onPressed: widget.onLoginTapped, - child: Center( - child: Text( - context.localization.actionLogin, - style: context.theme.textStyle( - textStyle: TextStyles.body2, - color: context.theme.colors.prominent, + Semantics( + explicitChildNodes: true, + label: SemanticsLabels.starterPageLoginButtonSemantic, + child: SecondaryButton( + onPressed: widget.onLoginTapped, + child: Center( + child: Text( + context.localization.actionLogin, + style: context.theme.textStyle( + textStyle: TextStyles.body2, + color: context.theme.colors.prominent, + ), ), ), ), diff --git a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart index 0bdb7ae3d..9671a72a8 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart @@ -4,6 +4,7 @@ import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_auth_ui/deriv_auth_ui.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; +import 'package:deriv_auth_ui/src/core/helpers/semantic_labels.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_divider.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_panel.dart'; import 'package:deriv_theme/deriv_theme.dart'; @@ -158,6 +159,7 @@ class _DerivLoginLayoutState extends State { List _buildTextFields({required bool isEnabled}) => [ BaseTextField( + semanticLabel: SemanticsLabels.loginEmailFieldSemantic, controller: _emailController, focusNode: _emailFocusNode, labelText: context.localization.labelEmail, @@ -172,6 +174,7 @@ class _DerivLoginLayoutState extends State { ), const SizedBox(height: ThemeProvider.margin32), BaseTextField( + semanticLabel: SemanticsLabels.loginPasswordFieldSemantic, controller: _passwordController, focusNode: _passwordFocusNode, labelText: context.localization.labelPassword, diff --git a/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_reset_pass_layout.dart b/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_reset_pass_layout.dart index 1182ead53..0a4ee0159 100644 --- a/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_reset_pass_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/reset_pass/layouts/deriv_reset_pass_layout.dart @@ -4,6 +4,7 @@ import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; import 'package:deriv_auth_ui/src/core/helpers/assets.dart'; +import 'package:deriv_auth_ui/src/core/helpers/semantic_labels.dart'; import 'package:deriv_theme/deriv_theme.dart'; import 'package:deriv_ui/deriv_ui.dart'; import 'package:flutter/material.dart'; @@ -135,6 +136,7 @@ class _DerivResetPassLayoutState extends State { ), const SizedBox(height: ThemeProvider.margin24), BaseTextField( + semanticLabel: SemanticsLabels.resetPasswordEmailFieldSemantic, controller: _emailController, focusNode: _emailFocusNode, labelText: context.localization.labelEmail, @@ -152,32 +154,36 @@ class _DerivResetPassLayoutState extends State { ), ); - Widget _buildSubmitEmailButton() => ElevatedButton( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - context.theme.colors.coral.withOpacity( - getOpacity(isEnabled: _isFormValid()), + Widget _buildSubmitEmailButton() => Semantics( + explicitChildNodes: true, + label: SemanticsLabels.resetPasswordButtonSemantic, + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + context.theme.colors.coral.withOpacity( + getOpacity(isEnabled: _isFormValid()), + ), ), ), - ), - onPressed: _isFormValid() ? _onSubmitEmailTapped : null, - child: Center( - child: _isBusy - ? LoadingIndicator( - valueColor: context.theme.colors.prominent, - strokeWidth: ThemeProvider.margin02, - height: ThemeProvider.margin16, - width: ThemeProvider.margin16, - ) - : Text( - context.localization.actionResetPass, - style: context.theme.textStyle( - textStyle: TextStyles.body2, - color: context.theme.colors.prominent.withOpacity( - getOpacity(isEnabled: _isFormValid()), + onPressed: _isFormValid() ? _onSubmitEmailTapped : null, + child: Center( + child: _isBusy + ? LoadingIndicator( + valueColor: context.theme.colors.prominent, + strokeWidth: ThemeProvider.margin02, + height: ThemeProvider.margin16, + width: ThemeProvider.margin16, + ) + : Text( + context.localization.actionResetPass, + style: context.theme.textStyle( + textStyle: TextStyles.body2, + color: context.theme.colors.prominent.withOpacity( + getOpacity(isEnabled: _isFormValid()), + ), ), ), - ), + ), ), ); diff --git a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart index 2a34b8396..6bce2b9c8 100644 --- a/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/signup/layouts/deriv_signup_layout.dart @@ -1,6 +1,7 @@ import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_auth_ui/src/core/extensions/context_extension.dart'; import 'package:deriv_auth_ui/src/core/extensions/string_extension.dart'; +import 'package:deriv_auth_ui/src/core/helpers/semantic_labels.dart'; import 'package:deriv_auth_ui/src/core/states/auth_state_listener.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_divider.dart'; import 'package:deriv_auth_ui/src/features/login/widgets/deriv_social_auth_panel.dart'; @@ -212,6 +213,8 @@ class _DerivSignupLayoutState extends State { vertical: ThemeProvider.margin16, ), child: BaseTextField( + semanticLabel: + SemanticsLabels.signupReferralTextFieldSemantic, controller: referralController, onChanged: (_) { if (mounted) { @@ -276,6 +279,7 @@ class _DerivSignupLayoutState extends State { ); Widget _buildEmailTextField() => BaseTextField( + semanticLabel: SemanticsLabels.signupEmailFieldSemantic, controller: emailController, focusNode: emailFocusNode, labelText: context.localization.labelEmail, @@ -291,25 +295,28 @@ class _DerivSignupLayoutState extends State { }, ); - Widget _buildSignUpButton() => PrimaryButton( - isEnabled: _isFormValid(), - onPressed: _onSignupTapped, - child: Center( - child: - context.read().state is DerivSignupProgressState - ? const LoadingIndicator( - valueColor: Colors.white, - strokeWidth: ThemeProvider.margin02, - height: ThemeProvider.iconSize16, - width: ThemeProvider.iconSize16, - ) - : Text( - context.localization.actionCreateAccount, - style: context.theme.textStyle( - textStyle: TextStyles.body2, - color: context.theme.colors.prominent, - ), + Widget _buildSignUpButton() => Semantics( + label: SemanticsLabels.signupButtonSemantic, + child: PrimaryButton( + isEnabled: _isFormValid(), + onPressed: _onSignupTapped, + child: Center( + child: context.read().state + is DerivSignupProgressState + ? const LoadingIndicator( + valueColor: Colors.white, + strokeWidth: ThemeProvider.margin02, + height: ThemeProvider.iconSize16, + width: ThemeProvider.iconSize16, + ) + : Text( + context.localization.actionCreateAccount, + style: context.theme.textStyle( + textStyle: TextStyles.body2, + color: context.theme.colors.prominent, ), + ), + ), ), ); diff --git a/packages/deriv_ui/lib/presentation/widgets/base_text_field.dart b/packages/deriv_ui/lib/presentation/widgets/base_text_field.dart index 60a9ee1c9..7aeeff8e6 100644 --- a/packages/deriv_ui/lib/presentation/widgets/base_text_field.dart +++ b/packages/deriv_ui/lib/presentation/widgets/base_text_field.dart @@ -32,6 +32,7 @@ class BaseTextField extends StatefulWidget { this.onEditingComplete, this.onChanged, this.onTap, + this.semanticLabel, Key? key, }) : super(key: key); @@ -113,6 +114,9 @@ class BaseTextField extends StatefulWidget { /// onTap callback function. final VoidCallback? onTap; + /// Semantic label. + final String? semanticLabel; + @override _BaseTextFieldState createState() => _BaseTextFieldState(); } @@ -132,63 +136,66 @@ class _BaseTextFieldState extends State { } @override - Widget build(BuildContext context) => TextFormField( - key: _formFieldKey, - controller: widget.controller, - focusNode: widget.focusNode, - initialValue: widget.initialValue, - keyboardType: widget.keyboardType, - maxLength: widget.maxLength, - autocorrect: false, - readOnly: widget.readOnly, - enabled: widget.enabled, - obscureText: widget.obscureText, - textInputAction: widget.textInputAction, - inputFormatters: widget.inputFormatters, - decoration: InputDecoration( - labelText: widget.labelText, - border: const OutlineInputBorder(), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: widget.borderColor ?? context.theme.colors.hover, + Widget build(BuildContext context) => Semantics( + explicitChildNodes: true, + label: widget.semanticLabel, + child: TextFormField( + key: _formFieldKey, + controller: widget.controller, + focusNode: widget.focusNode, + initialValue: widget.initialValue, + keyboardType: widget.keyboardType, + maxLength: widget.maxLength, + autocorrect: false, + readOnly: widget.readOnly, + enabled: widget.enabled, + obscureText: widget.obscureText, + textInputAction: widget.textInputAction, + inputFormatters: widget.inputFormatters, + decoration: InputDecoration( + labelText: widget.labelText, + border: const OutlineInputBorder(), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: widget.borderColor ?? context.theme.colors.hover, + ), + borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), ), - borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: widget.focusedBorderColor ?? context.theme.colors.blue, + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: widget.focusedBorderColor ?? context.theme.colors.blue, + ), ), - borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), - ), - errorBorder: OutlineInputBorder( - borderSide: BorderSide(color: context.theme.colors.coral), - borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), - ), - focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide(color: context.theme.colors.coral), - borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), - ), - labelStyle: context.theme.textStyle( - textStyle: TextStyles.subheading, - color: _hasError - ? context.theme.colors.coral - : _hasFocus() - ? widget.focusedLabelColor ?? context.theme.colors.blue - : widget.labelColor ?? context.theme.colors.disabled, - ), - counterText: widget.showCounterText ? null : '', - errorMaxLines: widget.errorMaxLines, - prefix: widget.prefix, - prefixStyle: context.theme.textStyle( - textStyle: TextStyles.subheading, - color: _getTextFieldColor(), + errorBorder: OutlineInputBorder( + borderSide: BorderSide(color: context.theme.colors.coral), + borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide(color: context.theme.colors.coral), + borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), + ), + labelStyle: context.theme.textStyle( + textStyle: TextStyles.subheading, + color: _hasError + ? context.theme.colors.coral + : _hasFocus() + ? widget.focusedLabelColor ?? context.theme.colors.blue + : widget.labelColor ?? context.theme.colors.disabled, + ), + counterText: widget.showCounterText ? null : '', + errorMaxLines: widget.errorMaxLines, + prefix: widget.prefix, + prefixStyle: context.theme.textStyle( + textStyle: TextStyles.subheading, + color: _getTextFieldColor(), + ), + suffixIcon: widget.suffixIcon, ), - suffixIcon: widget.suffixIcon, + onEditingComplete: widget.onEditingComplete, + validator: _validator, + onChanged: _onChanged, + onTap: widget.onTap, ), - onEditingComplete: widget.onEditingComplete, - validator: _validator, - onChanged: _onChanged, - onTap: widget.onTap, ); bool _hasFocus() => widget.focusNode?.hasFocus ?? false; From 1d842a867b32c01a917445ca3748c8ce34dbbcec Mon Sep 17 00:00:00 2001 From: mobile-apps-deriv <134251399+mobile-apps-deriv@users.noreply.github.com> Date: Wed, 29 Nov 2023 11:06:09 +0800 Subject: [PATCH 41/44] chore(release): publish packages (#345) - deriv_auth_ui@0.0.1+2 - deriv_ui@0.0.1+2 Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ packages/deriv_auth_ui/CHANGELOG.md | 4 ++++ packages/deriv_auth_ui/pubspec.yaml | 4 ++-- packages/deriv_ui/CHANGELOG.md | 4 ++++ packages/deriv_ui/pubspec.yaml | 2 +- 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e1ca4fc7..6fc1ad8af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,32 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2023-11-29 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_auth_ui` - `v0.0.1+2`](#deriv_auth_ui---v0012) + - [`deriv_ui` - `v0.0.1+2`](#deriv_ui---v0012) + +--- + +#### `deriv_auth_ui` - `v0.0.1+2` + + - **REFACTOR**(deriv_auth_ui): [MOBC-629] Adding semantics to UI components ([#321](https://github.com/regentmarkets/flutter-deriv-packages/issues/321)). ([36a0c1fa](https://github.com/regentmarkets/flutter-deriv-packages/commit/36a0c1faa0b47d4f735a79daf67c9e2c0089938e)) + +#### `deriv_ui` - `v0.0.1+2` + + - **REFACTOR**(deriv_auth_ui): [MOBC-629] Adding semantics to UI components ([#321](https://github.com/regentmarkets/flutter-deriv-packages/issues/321)). ([36a0c1fa](https://github.com/regentmarkets/flutter-deriv-packages/commit/36a0c1faa0b47d4f735a79daf67c9e2c0089938e)) + + ## 2023-11-28 ### Changes diff --git a/packages/deriv_auth_ui/CHANGELOG.md b/packages/deriv_auth_ui/CHANGELOG.md index cf1939e79..d4e657e71 100644 --- a/packages/deriv_auth_ui/CHANGELOG.md +++ b/packages/deriv_auth_ui/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+2 + + - **REFACTOR**(deriv_auth_ui): [MOBC-629] Adding semantics to UI components ([#321](https://github.com/regentmarkets/flutter-deriv-packages/issues/321)). ([36a0c1fa](https://github.com/regentmarkets/flutter-deriv-packages/commit/36a0c1faa0b47d4f735a79daf67c9e2c0089938e)) + ## 0.0.1+1 - Update a dependency to the latest release. diff --git a/packages/deriv_auth_ui/pubspec.yaml b/packages/deriv_auth_ui/pubspec.yaml index 7b54f8635..30eb330ee 100644 --- a/packages/deriv_auth_ui/pubspec.yaml +++ b/packages/deriv_auth_ui/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_auth_ui description: A flutter package for deriv auth UI flows. -version: 0.0.1+1 +version: 0.0.1+2 environment: sdk: ">=3.0.0 <4.0.0" @@ -28,7 +28,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: deriv_ui-v0.0.1+1 + ref: deriv_ui-v0.0.1+2 flutter_svg: ^1.1.6 smooth_page_indicator: ^1.1.0 intl_utils: ^2.8.3 diff --git a/packages/deriv_ui/CHANGELOG.md b/packages/deriv_ui/CHANGELOG.md index b7f2af439..2a58aaa09 100644 --- a/packages/deriv_ui/CHANGELOG.md +++ b/packages/deriv_ui/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+2 + + - **REFACTOR**(deriv_auth_ui): [MOBC-629] Adding semantics to UI components ([#321](https://github.com/regentmarkets/flutter-deriv-packages/issues/321)). ([36a0c1fa](https://github.com/regentmarkets/flutter-deriv-packages/commit/36a0c1faa0b47d4f735a79daf67c9e2c0089938e)) + ## 0.0.1+1 - Update a dependency to the latest release. diff --git a/packages/deriv_ui/pubspec.yaml b/packages/deriv_ui/pubspec.yaml index a804b66a8..8201c1b5a 100644 --- a/packages/deriv_ui/pubspec.yaml +++ b/packages/deriv_ui/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_ui description: A new Flutter package project. -version: 0.0.1+1 +version: 0.0.1+2 publish_to: none environment: From 4d212e0e444a4a77f8fad3894e67375f5dae58ae Mon Sep 17 00:00:00 2001 From: anas-deriv Date: Fri, 8 Dec 2023 16:09:48 +0800 Subject: [PATCH 42/44] add option to enable/disable forgot password & create account --- .../lib/features/login/pages/login_page.dart | 3 +++ .../login/layouts/deriv_login_layout.dart | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart b/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart index 2c6ad3518..2d06e02fd 100644 --- a/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart +++ b/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart @@ -24,6 +24,9 @@ class _LoginPageState extends State { @override Widget build(BuildContext context) { return DerivLoginLayout( + isShowCreateAccount: false, + isShowForgotPassword: false, + isSocialAuthEnabled: false, welcomeLabel: 'Welcome back!', greetingLabel: 'Log in to your Deriv account to start trading and investing.', diff --git a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart index 9671a72a8..e933eaf9a 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart @@ -22,6 +22,8 @@ class DerivLoginLayout extends StatefulWidget { required this.onSocialAuthButtonPressed, required this.welcomeLabel, required this.greetingLabel, + this.isShowForgotPassword = true, + this.isShowCreateAccount = true, this.isSocialAuthEnabled = true, this.authErrorStateHandler, this.onLoginError, @@ -59,6 +61,12 @@ class DerivLoginLayout extends StatefulWidget { /// Whether to display social auth buttons. final bool isSocialAuthEnabled; + /// Whether to display forgot password section. + final bool isShowForgotPassword; + + /// Whether to display create account section. + final bool isShowCreateAccount; + @override State createState() => _DerivLoginLayoutState(); } @@ -112,7 +120,9 @@ class _DerivLoginLayoutState extends State { ..._buildTextFields( isEnabled: state is! DerivAuthLoadingState), const SizedBox(height: ThemeProvider.margin24), - _buildForgotPassButton(), + widget.isShowForgotPassword + ? _buildForgotPassButton() + : const SizedBox(), const SizedBox(height: ThemeProvider.margin24), _buildLoginButton(), const SizedBox(height: ThemeProvider.margin24), @@ -129,7 +139,9 @@ class _DerivLoginLayoutState extends State { ), if (widget.isSocialAuthEnabled) const SizedBox(height: ThemeProvider.margin24), - _buildFooterSection(), + widget.isShowCreateAccount + ? _buildFooterSection() + : const SizedBox(), ], ), ), From 37a63911de1b25935c609cfb6c1f8aee6e1d5c58 Mon Sep 17 00:00:00 2001 From: anas-deriv Date: Wed, 13 Dec 2023 14:54:23 +0800 Subject: [PATCH 43/44] fix comment from @sahani-deriv --- .../example/lib/features/login/pages/login_page.dart | 3 --- .../features/login/layouts/deriv_login_layout.dart | 12 ++++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart b/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart index 2d06e02fd..2c6ad3518 100644 --- a/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart +++ b/packages/deriv_auth_ui/example/lib/features/login/pages/login_page.dart @@ -24,9 +24,6 @@ class _LoginPageState extends State { @override Widget build(BuildContext context) { return DerivLoginLayout( - isShowCreateAccount: false, - isShowForgotPassword: false, - isSocialAuthEnabled: false, welcomeLabel: 'Welcome back!', greetingLabel: 'Log in to your Deriv account to start trading and investing.', diff --git a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart index e933eaf9a..a2338bf8e 100644 --- a/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart +++ b/packages/deriv_auth_ui/lib/src/features/login/layouts/deriv_login_layout.dart @@ -22,8 +22,8 @@ class DerivLoginLayout extends StatefulWidget { required this.onSocialAuthButtonPressed, required this.welcomeLabel, required this.greetingLabel, - this.isShowForgotPassword = true, - this.isShowCreateAccount = true, + this.isForgotPasswordEnabled = true, + this.isCreateAccountEnabled = true, this.isSocialAuthEnabled = true, this.authErrorStateHandler, this.onLoginError, @@ -62,10 +62,10 @@ class DerivLoginLayout extends StatefulWidget { final bool isSocialAuthEnabled; /// Whether to display forgot password section. - final bool isShowForgotPassword; + final bool isForgotPasswordEnabled; /// Whether to display create account section. - final bool isShowCreateAccount; + final bool isCreateAccountEnabled; @override State createState() => _DerivLoginLayoutState(); @@ -120,7 +120,7 @@ class _DerivLoginLayoutState extends State { ..._buildTextFields( isEnabled: state is! DerivAuthLoadingState), const SizedBox(height: ThemeProvider.margin24), - widget.isShowForgotPassword + widget.isForgotPasswordEnabled ? _buildForgotPassButton() : const SizedBox(), const SizedBox(height: ThemeProvider.margin24), @@ -139,7 +139,7 @@ class _DerivLoginLayoutState extends State { ), if (widget.isSocialAuthEnabled) const SizedBox(height: ThemeProvider.margin24), - widget.isShowCreateAccount + widget.isCreateAccountEnabled ? _buildFooterSection() : const SizedBox(), ], From 7603506cf3e126e398db77e79ce0545b93e8b348 Mon Sep 17 00:00:00 2001 From: anas-deriv Date: Fri, 15 Dec 2023 11:01:41 +0800 Subject: [PATCH 44/44] update dependencies --- packages/deriv_auth_ui/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/deriv_auth_ui/pubspec.yaml b/packages/deriv_auth_ui/pubspec.yaml index 30eb330ee..ffc93405f 100644 --- a/packages/deriv_auth_ui/pubspec.yaml +++ b/packages/deriv_auth_ui/pubspec.yaml @@ -23,12 +23,12 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme - ref: deriv_theme-v2.1.0 + ref: deriv_theme-v2.2.0 deriv_ui: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: deriv_ui-v0.0.1+2 + ref: deriv_ui-v0.0.2 flutter_svg: ^1.1.6 smooth_page_indicator: ^1.1.0 intl_utils: ^2.8.3