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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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/63] 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 a27baeb625d5f74b60b5fc06f0559335ca9f30fa Mon Sep 17 00:00:00 2001 From: Ahrar <98078754+ahrar-deriv@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:16:47 +0800 Subject: [PATCH 42/63] docs: add semantic versioning to git rules doc (#346) * docs: add semantic versioning document * docs: styling the git rules md file * docs: GIT_RULES style * chore: Update .github/GIT_RULES.md add refactor Co-authored-by: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> * chore: add missing format GIT_RULES.md --------- Co-authored-by: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> --- .github/GIT_RULES.md | 58 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/.github/GIT_RULES.md b/.github/GIT_RULES.md index 2307dd09e..584dacbfc 100644 --- a/.github/GIT_RULES.md +++ b/.github/GIT_RULES.md @@ -1,5 +1,4 @@ -## Commit Rules: - +## 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: @@ -30,12 +29,7 @@ feat(deriv_auth): add UI for sign in page - 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: - +More changes types: | Changes Types | Meaning | Description | | ------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------- | | feat | Features | A new feature | @@ -94,7 +88,53 @@ Optional: If you would like to pre populate your commit box with the commit temp * ``` style: remove empty line ``` -## PR Rules: +### 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 + + +

 

+ +## Semantic versioning: +
1 . 4 . 3 + 2
+
Major . Minor . Patch + build
+

 

+ +Major Release: +If a ```“breaking change”``` is introduced, the major release number must be increased + +``` +feat(api)!: remove status endpoint +fix!: bug fix with breaking change +``` + +Minor Release: +New features have been introduced, which are backwards compatible ```no “breaking changes”``` + + +``` +feat: new feature +``` + +Patch Release: +Bug fixes ```no “breaking changes”```
+``` +fix: something in code +refactor: code changes that doesn't fix or add anything +``` + +Build: This number is optional and can be used to differentiate between different builds of the same version. +
+ +no change | | build bump: +``` +build, chore, ci, docs, style, perf, test +``` +

 

+ +## 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: From 37f5b763d8a679ff1d458fc4e9b82578f4eecf83 Mon Sep 17 00:00:00 2001 From: zohreh <108648307+zohreh-deriv@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:45:28 +0800 Subject: [PATCH 43/63] feat(deriv_auth): [DERG-1299] add user agent to login (#341) * feat(deriv_auth): add user agent add optional user agent for login requests * feat(deriv_auth)add user agent to get user tokens request header * fix(deriv_auth): User-Agent typo * fix(deriv_auth): add missing parameter * fix(deriv_auth): add default value for user agent parameter --------- Co-authored-by: Ahrar --- .../token/services/base_token_service.dart | 1 + .../token/services/deriv_token_service.dart | 6 ++- .../features/auth/cubit/deriv_auth_cubit.dart | 36 +++++++++++------- .../auth/services/base_auth_service.dart | 7 ++-- .../auth/services/deriv_auth_service.dart | 12 +++--- .../auth/cubit/deriv_auth_cubit_test.dart | 38 ++++++++++++------- .../auth/service/deriv_auth_service_test.dart | 31 +++++++-------- 7 files changed, 80 insertions(+), 51 deletions(-) diff --git a/packages/deriv_auth/lib/core/services/token/services/base_token_service.dart b/packages/deriv_auth/lib/core/services/token/services/base_token_service.dart index c9030276c..56f251c2e 100644 --- a/packages/deriv_auth/lib/core/services/token/services/base_token_service.dart +++ b/packages/deriv_auth/lib/core/services/token/services/base_token_service.dart @@ -11,5 +11,6 @@ abstract class BaseTokenService { required BaseHttpClient client, required String jwtToken, required AuthConnectionInfo connectionInfo, + String? userAgent, }); } diff --git a/packages/deriv_auth/lib/core/services/token/services/deriv_token_service.dart b/packages/deriv_auth/lib/core/services/token/services/deriv_token_service.dart index 949de9c0a..2e3028672 100644 --- a/packages/deriv_auth/lib/core/services/token/services/deriv_token_service.dart +++ b/packages/deriv_auth/lib/core/services/token/services/deriv_token_service.dart @@ -12,6 +12,7 @@ class DerivTokenService implements BaseTokenService { required BaseHttpClient client, required String jwtToken, required AuthConnectionInfo connectionInfo, + String? userAgent, }) async { /// Extract login url from connection info. final String baseUrl = 'https://${connectionInfo.endpoint}/oauth2/api/v1'; @@ -22,7 +23,10 @@ class DerivTokenService implements BaseTokenService { url: loginUrl, jsonBody: request.copyWith(appId: int.parse(connectionInfo.appId)).toJson(), - headers: {'Authorization': 'Bearer $jwtToken'}, + headers: { + 'Authorization': 'Bearer $jwtToken', + 'User-Agent': userAgent ?? 'Dart/3.0 (dart:io)' + }, ); return GetTokensResponseModel.fromJson(jsonResponse); diff --git a/packages/deriv_auth/lib/features/auth/cubit/deriv_auth_cubit.dart b/packages/deriv_auth/lib/features/auth/cubit/deriv_auth_cubit.dart index d963069c9..481787c6d 100644 --- a/packages/deriv_auth/lib/features/auth/cubit/deriv_auth_cubit.dart +++ b/packages/deriv_auth/lib/features/auth/cubit/deriv_auth_cubit.dart @@ -29,17 +29,19 @@ class DerivAuthCubit extends Cubit implements DerivAuthIO { required String email, required String password, String? otp, + String? userAgent, }) async { emit(DerivAuthLoadingState()); await _loginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.system, email: email, password: password, otp: otp, ), - false, + isSocialLogin: false, + userAgent: userAgent, ); } @@ -48,17 +50,19 @@ class DerivAuthCubit extends Cubit implements DerivAuthIO { required String oneAllConnectionToken, final String? signupProvider, String? otp, + String? userAgent, }) async { emit(DerivAuthLoadingState()); await _loginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.social, oneAllConnectionToken: oneAllConnectionToken, signupProvider: signupProvider, otp: otp, ), - true, + isSocialLogin: true, + userAgent: userAgent, ); } @@ -66,16 +70,18 @@ class DerivAuthCubit extends Cubit implements DerivAuthIO { Future socialAuth({ required SocialAuthDto socialAuthDto, String? otp, + String? userAgent, }) async { emit(DerivAuthLoadingState()); await _loginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.socialLogin, socialAuthDto: socialAuthDto, otp: otp, ), - true, + isSocialLogin: true, + userAgent: userAgent, ); } @@ -89,11 +95,16 @@ class DerivAuthCubit extends Cubit implements DerivAuthIO { ); } - Future _loginRequest( - GetTokensRequestModel request, bool isSocialLogin) async { + Future _loginRequest({ + required GetTokensRequestModel request, + required bool isSocialLogin, + String? userAgent, + }) async { try { - final AuthorizeEntity authorizeEntity = - await authService.onLoginRequest(request); + final AuthorizeEntity authorizeEntity = await authService.onLoginRequest( + request: request, + userAgent: userAgent, + ); final LandingCompanyEntity landingCompanyEntity = await authService.getLandingCompany(authorizeEntity.country); _isUserMigrated = _checkUserMigrated(authorizeEntity); @@ -182,10 +193,9 @@ class DerivAuthCubit extends Cubit implements DerivAuthIO { bool get isMigratedToWallets => _isUserMigrated; bool _checkUserMigrated(AuthorizeEntity authorizeEntity) => - authorizeEntity.accountList?.any( - (AccountListItem account) => + authorizeEntity.accountList?.any((AccountListItem account) => account.accountCategory == AccountCategoryEnum.wallet) ?? - false; + false; @override Stream get output => stream; diff --git a/packages/deriv_auth/lib/features/auth/services/base_auth_service.dart b/packages/deriv_auth/lib/features/auth/services/base_auth_service.dart index e8304cb80..f3df80e13 100644 --- a/packages/deriv_auth/lib/features/auth/services/base_auth_service.dart +++ b/packages/deriv_auth/lib/features/auth/services/base_auth_service.dart @@ -6,9 +6,10 @@ import 'package:deriv_auth/core/services/token/models/login_request.dart'; /// Interface to define all authentication-related functionality. abstract class BaseAuthService { /// Function before logging user in. - Future onLoginRequest( - GetTokensRequestModel request, - ); + Future onLoginRequest({ + required GetTokensRequestModel request, + String? userAgent, + }); /// Log in a user with [token]. Future login( 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 3aabac973..6c2db5a5d 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 @@ -40,10 +40,11 @@ class DerivAuthService extends BaseAuthService { final BaseTokenService tokenService; @override - Future onLoginRequest( - GetTokensRequestModel request, [ + Future onLoginRequest({ + required GetTokensRequestModel request, + String? userAgent, Function? onInvalidJwtToken, - ]) async { + }) async { try { final String jwtToken = await jwtService.getJwtToken(); @@ -52,6 +53,7 @@ class DerivAuthService extends BaseAuthService { client: HttpClient(), jwtToken: jwtToken, connectionInfo: connectionInfo, + userAgent: userAgent, ); final List _supportedAccounts = @@ -78,7 +80,7 @@ class DerivAuthService extends BaseAuthService { jwtService.clearJwtToken(); - return onLoginRequest(request); + return onLoginRequest(request: request, userAgent: userAgent); } else { throw _mapHttpErrorToDerivAuthError(error); } @@ -200,7 +202,7 @@ class DerivAuthService extends BaseAuthService { return DerivAuthException( type: AuthErrorType.invalidResidence, message: exception.message, - ); + ); default: return DerivAuthException( diff --git a/packages/deriv_auth/test/features/auth/cubit/deriv_auth_cubit_test.dart b/packages/deriv_auth/test/features/auth/cubit/deriv_auth_cubit_test.dart index 0c3254c1b..73829497f 100644 --- a/packages/deriv_auth/test/features/auth/cubit/deriv_auth_cubit_test.dart +++ b/packages/deriv_auth/test/features/auth/cubit/deriv_auth_cubit_test.dart @@ -90,8 +90,9 @@ void main() { () { registerFallbackValue(GetTokensRequestModel(type: AuthType.system)); - when(() => service.onLoginRequest(any())).thenAnswer( - (_) => Future.value(mockedValidAuthorizeEntity)); + when(() => service.onLoginRequest(request: any(named: 'request'))) + .thenAnswer((_) => + Future.value(mockedValidAuthorizeEntity)); final List> expectedResponse = >[ @@ -106,7 +107,8 @@ void main() { authCubit.systemLogin(email: 'email', password: 'password'); - verify(() => service.onLoginRequest(any())).called(1); + verify(() => service.onLoginRequest(request: any(named: 'request'))) + .called(1); }); test( @@ -114,8 +116,9 @@ void main() { () { registerFallbackValue(GetTokensRequestModel(type: AuthType.social)); - when(() => service.onLoginRequest(any())).thenAnswer( - (_) => Future.value(mockedValidAuthorizeEntity)); + when(() => service.onLoginRequest(request: any(named: 'request'))) + .thenAnswer((_) => + Future.value(mockedValidAuthorizeEntity)); final List> expectedResponse = >[ @@ -130,7 +133,8 @@ void main() { authCubit.socialLogin(oneAllConnectionToken: 'token'); - verify(() => service.onLoginRequest(any())).called(1); + verify(() => service.onLoginRequest(request: any(named: 'request'))) + .called(1); }); test( @@ -138,8 +142,9 @@ void main() { () { registerFallbackValue(GetTokensRequestModel(type: AuthType.social)); - when(() => service.onLoginRequest(any())).thenAnswer( - (_) => Future.value(mockedValidAuthorizeEntity)); + when(() => service.onLoginRequest(request: any(named: 'request'))) + .thenAnswer((_) => + Future.value(mockedValidAuthorizeEntity)); final List> expectedResponse = >[ @@ -156,12 +161,14 @@ void main() { socialAuthDto: mockSocialAuthDto, ); - verify(() => service.onLoginRequest(any())).called(1); + verify(() => service.onLoginRequest(request: any(named: 'request'))) + .called(1); }); test('should emit [AuthLoggedInState] upon a successful otp login.', () { - when(() => service.onLoginRequest(any())).thenAnswer( - (_) => Future.value(mockedValidAuthorizeEntity)); + when(() => service.onLoginRequest(request: any(named: 'request'))) + .thenAnswer((_) => + Future.value(mockedValidAuthorizeEntity)); final List> expectedResponse = >[ @@ -175,14 +182,16 @@ void main() { ); authCubit.systemLogin(email: 'email', password: 'pass', otp: 'otp'); - verify(() => service.onLoginRequest(any())).called(1); + verify(() => service.onLoginRequest(request: any(named: 'request'))) + .called(1); }); test('should emit [AuthErrorState] when an exception occurs in service.', () { registerFallbackValue(GetTokensRequestModel(type: AuthType.system)); - when(() => service.onLoginRequest(any())).thenThrow(DerivAuthException( + when(() => service.onLoginRequest(request: any(named: 'request'))) + .thenThrow(DerivAuthException( message: 'message', type: AuthErrorType.expiredAccount, )); @@ -200,7 +209,8 @@ void main() { authCubit.systemLogin(email: 'email', password: 'pass'); - verify(() => service.onLoginRequest(any())).called(1); + verify(() => service.onLoginRequest(request: any(named: 'request'))) + .called(1); }); test('should emit [AuthLoggedOutState] upon a successful logout.', () { when(() => service.logout()).thenAnswer((_) => Future.value()); diff --git a/packages/deriv_auth/test/features/auth/service/deriv_auth_service_test.dart b/packages/deriv_auth/test/features/auth/service/deriv_auth_service_test.dart index 3e77e295a..43541c07d 100644 --- a/packages/deriv_auth/test/features/auth/service/deriv_auth_service_test.dart +++ b/packages/deriv_auth/test/features/auth/service/deriv_auth_service_test.dart @@ -127,7 +127,7 @@ void main() { ); final AuthorizeEntity response = await authService.onLoginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.system, email: 'email', password: 'pass', @@ -154,7 +154,7 @@ void main() { ); final AuthorizeEntity response = await authService.onLoginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.social, signupProvider: 'signupProvider', oneAllConnectionToken: 'oneAllConnectionToken', @@ -180,7 +180,7 @@ void main() { ); final AuthorizeEntity response = await authService.onLoginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.socialLogin, signupProvider: 'signupProvider', socialAuthDto: mockSocialAuthDto, @@ -207,16 +207,17 @@ void main() { ); final AuthorizeEntity response = await authService.onLoginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.system, email: 'email', password: 'pass', signupProvider: 'signupProvider', - ), () { - when(() => jwtService.getJwtToken()).thenAnswer( - (_) => Future.value(validJwtToken), - ); - }); + ), + onInvalidJwtToken: () { + when(() => jwtService.getJwtToken()).thenAnswer( + (_) => Future.value(validJwtToken), + ); + }); verify(() => jwtService.getJwtToken()).called(2); @@ -251,7 +252,7 @@ void main() { expect( authService.onLoginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.system, email: 'email', password: 'pass', @@ -288,7 +289,7 @@ void main() { expect( authService.onLoginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.system, email: 'email', password: 'pass', @@ -325,7 +326,7 @@ void main() { expect( authService.onLoginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.system, email: 'email', password: 'pass', @@ -434,7 +435,7 @@ void main() { expect( authService.onLoginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.system, email: 'email', password: 'pass', @@ -471,7 +472,7 @@ void main() { expect( authService.onLoginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.system, email: 'email', password: 'pass', @@ -508,7 +509,7 @@ void main() { expect( authService.onLoginRequest( - GetTokensRequestModel( + request: GetTokensRequestModel( type: AuthType.system, email: 'email', password: 'pass', From cc5a53f52487a4994d243a87b544ee6ca9f93538 Mon Sep 17 00:00:00 2001 From: mobile-apps-deriv <134251399+mobile-apps-deriv@users.noreply.github.com> Date: Mon, 4 Dec 2023 15:35:47 +0800 Subject: [PATCH 44/63] chore(release): publish packages (#348) - deriv_auth@1.1.0 - deriv_auth_ui@0.0.1+3 Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ packages/deriv_auth/CHANGELOG.md | 4 ++++ packages/deriv_auth/pubspec.yaml | 2 +- packages/deriv_auth_ui/CHANGELOG.md | 4 ++++ packages/deriv_auth_ui/pubspec.yaml | 4 ++-- 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fc1ad8af..9c95b0856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,34 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2023-12-04 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_auth` - `v1.1.0`](#deriv_auth---v110) + - [`deriv_auth_ui` - `v0.0.1+3`](#deriv_auth_ui---v0013) + +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_auth_ui` - `v0.0.1+3` + +--- + +#### `deriv_auth` - `v1.1.0` + + - **FEAT**(deriv_auth): [DERG-1299] add user agent to login ([#341](https://github.com/regentmarkets/flutter-deriv-packages/issues/341)). ([37f5b763](https://github.com/regentmarkets/flutter-deriv-packages/commit/37f5b763d8a679ff1d458fc4e9b82578f4eecf83)) + + ## 2023-11-29 ### Changes diff --git a/packages/deriv_auth/CHANGELOG.md b/packages/deriv_auth/CHANGELOG.md index effe43c82..65e189307 100644 --- a/packages/deriv_auth/CHANGELOG.md +++ b/packages/deriv_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.1.0 + + - **FEAT**(deriv_auth): [DERG-1299] add user agent to login ([#341](https://github.com/regentmarkets/flutter-deriv-packages/issues/341)). ([37f5b763](https://github.com/regentmarkets/flutter-deriv-packages/commit/37f5b763d8a679ff1d458fc4e9b82578f4eecf83)) + ## 1.0.0 - Initial version. diff --git a/packages/deriv_auth/pubspec.yaml b/packages/deriv_auth/pubspec.yaml index 0204a2199..8c8417d88 100644 --- a/packages/deriv_auth/pubspec.yaml +++ b/packages/deriv_auth/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_auth description: Provides deriv authentication functionalities for dart/flutter apps. -version: 1.0.2 +version: 1.1.0 publish_to: "none" diff --git a/packages/deriv_auth_ui/CHANGELOG.md b/packages/deriv_auth_ui/CHANGELOG.md index d4e657e71..9a9cea2da 100644 --- a/packages/deriv_auth_ui/CHANGELOG.md +++ b/packages/deriv_auth_ui/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+3 + + - Update a dependency to the latest release. + ## 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)) diff --git a/packages/deriv_auth_ui/pubspec.yaml b/packages/deriv_auth_ui/pubspec.yaml index 30eb330ee..b90543d4c 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+2 +version: 0.0.1+3 environment: sdk: ">=3.0.0 <4.0.0" @@ -18,7 +18,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_auth - ref: deriv_auth-v1.0.2 + ref: deriv_auth-v1.1.0 deriv_theme: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git From bf036f8be64bb957219f2f213a8eed2dc0f60279 Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Mon, 4 Dec 2023 16:14:02 +0800 Subject: [PATCH 45/63] add remote config source to update checker --- .../example/lib/update_bloc_page.dart | 3 +- .../example/lib/update_checker_page.dart | 1 + .../lib/src/bloc/update_bloc.dart | 54 +++++++++-- .../lib/src/presentation/update_checker.dart | 9 +- .../lib/src/repositories/firebase_base.dart | 6 ++ .../src/repositories/firebase_database.dart | 4 +- .../repositories/firebase_remote_config.dart | 19 ++++ .../lib/src/repositories/repositories.dart | 1 + packages/update_checker/pubspec.lock | 90 +++++++++++++------ packages/update_checker/pubspec.yaml | 1 + .../test/update_checker_test.dart | 7 +- 11 files changed, 156 insertions(+), 39 deletions(-) create mode 100644 packages/update_checker/lib/src/repositories/firebase_base.dart create mode 100644 packages/update_checker/lib/src/repositories/firebase_remote_config.dart diff --git a/packages/update_checker/example/lib/update_bloc_page.dart b/packages/update_checker/example/lib/update_bloc_page.dart index 9afd733fd..771661416 100644 --- a/packages/update_checker/example/lib/update_bloc_page.dart +++ b/packages/update_checker/example/lib/update_bloc_page.dart @@ -9,7 +9,8 @@ class UpdateBlocPage extends StatefulWidget { } class _UpdateBlocPageState extends State { - final UpdateBloc updateBloc = UpdateBloc(); + final UpdateBloc updateBloc = + UpdateBloc(fireBaseRepository: const FirebaseRemoteConfigRepository()); @override Widget build(BuildContext context) => Scaffold( diff --git a/packages/update_checker/example/lib/update_checker_page.dart b/packages/update_checker/example/lib/update_checker_page.dart index 1440f52a5..484e6fe61 100644 --- a/packages/update_checker/example/lib/update_checker_page.dart +++ b/packages/update_checker/example/lib/update_checker_page.dart @@ -10,6 +10,7 @@ class UpdateCheckerPage extends StatelessWidget { ), body: Builder( builder: (BuildContext context) => UpdateChecker( + fireBaseRepository: const FirebaseRemoteConfigRepository(), onNotAvailable: () => _showSnackBar( context, 'Update not available', diff --git a/packages/update_checker/lib/src/bloc/update_bloc.dart b/packages/update_checker/lib/src/bloc/update_bloc.dart index 84f3544a1..940db01c6 100644 --- a/packages/update_checker/lib/src/bloc/update_bloc.dart +++ b/packages/update_checker/lib/src/bloc/update_bloc.dart @@ -1,13 +1,16 @@ import 'dart:convert'; +import 'dart:io'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:update_checker/src/repositories/firebase_base.dart'; import '../models/models.dart'; import '../repositories/repositories.dart'; import '../utils/utils.dart'; part 'update_event.dart'; + part 'update_state.dart'; /// UpdateBloc is responsible for fetching the update availability from the @@ -17,7 +20,7 @@ class UpdateBloc extends Bloc { /// availability from the Firebase Database and `PackageInfoRepository` to /// check the app version with the update to determine the update availability UpdateBloc({ - this.firebaseDatabaseRepository = const FirebaseDatabaseRepository(), + required this.fireBaseRepository, this.packageInfoRepository = const PackageInfoRepository(), }) : super(UpdateInitialState()) { on( @@ -27,7 +30,7 @@ class UpdateBloc extends Bloc { } /// Firebase database repository for fetching the update information. - final FirebaseDatabaseRepository firebaseDatabaseRepository; + final FireBaseBase fireBaseRepository; /// Package info repository for fetching the app build number. final PackageInfoRepository packageInfoRepository; @@ -36,7 +39,10 @@ class UpdateBloc extends Bloc { UpdateFetchEvent event, Emitter emit) async { if (state is! UpdateInProgressState) { emit(UpdateInProgressState()); - final UpdateInfo? info = await _getUpdateInfo(); + final UpdateInfo? info = + (fireBaseRepository is FirebaseRemoteConfigRepository) + ? await _getUpdateInfoFromRemoteConfig() + : await _getUpdateInfoFromDataBase(); if (info != null) { emit(UpdateAvailableState(info)); } else { @@ -45,8 +51,46 @@ class UpdateBloc extends Bloc { } } - Future _getUpdateInfo() async { - final dynamic rawData = await firebaseDatabaseRepository.fetchUpdateData(); + Future _getUpdateInfoFromRemoteConfig() async { + final String rawData = await fireBaseRepository.fetchUpdateData(); + + // checks if failed to fetch data from Firebase Database and return + if (rawData.isEmpty) { + return null; + } + + // checks if failed to get app build number and return + final num appBuildNumber = await packageInfoRepository.getAppBuildNumber(); + if (appBuildNumber <= 0) { + return null; + } + + final Map mapValues = json.decode(rawData); + + final num minVersion = + mapValues[Platform.isAndroid ? 'android' : 'ios']['version']['minimum']; + final num latestVersion = + mapValues[Platform.isAndroid ? 'android' : 'ios']['version']['latest']; + + final bool isMandatory = appBuildNumber < minVersion; + + final bool isOptional = + appBuildNumber < latestVersion && appBuildNumber > minVersion; + + // checks if no update available and return + if (!isMandatory && !isOptional) { + return null; + } + + return _createUpdate( + mapValues[Platform.isAndroid ? 'android' : 'ios'], + isOptional, + isOptional ? latestVersion : minVersion, + ); + } + + Future _getUpdateInfoFromDataBase() async { + final dynamic rawData = await fireBaseRepository.fetchUpdateData(); // checks if failed to fetch data from Firebase Database and return if (rawData == null) { diff --git a/packages/update_checker/lib/src/presentation/update_checker.dart b/packages/update_checker/lib/src/presentation/update_checker.dart index 38659152b..337e73686 100644 --- a/packages/update_checker/lib/src/presentation/update_checker.dart +++ b/packages/update_checker/lib/src/presentation/update_checker.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:update_checker/src/repositories/firebase_base.dart'; import '../../update_checker.dart'; @@ -9,6 +10,7 @@ class UpdateChecker extends StatefulWidget { /// Checks for update availability as soon as renders and will call the proper /// callback based on the UpdateState. const UpdateChecker({ + required this.fireBaseRepository, this.child, this.onAvailable, this.onNotAvailable, @@ -18,6 +20,10 @@ class UpdateChecker extends StatefulWidget { /// The [child] that UpdateChecker widget will wrap. final Widget? child; + /// The [fireBaseRepository] that fetch the update information + /// from the firebase system. + final FireBaseBase fireBaseRepository; + /// [onAvailable] will be called when there is an update available. final Function(UpdateInfo)? onAvailable; @@ -32,11 +38,12 @@ class UpdateChecker extends StatefulWidget { } class _UpdateCheckerState extends State { - final UpdateBloc _updateBloc = UpdateBloc(); + late final UpdateBloc _updateBloc; @override void initState() { super.initState(); + _updateBloc = UpdateBloc(fireBaseRepository: widget.fireBaseRepository); _updateBloc.add(UpdateFetchEvent()); } diff --git a/packages/update_checker/lib/src/repositories/firebase_base.dart b/packages/update_checker/lib/src/repositories/firebase_base.dart new file mode 100644 index 000000000..c186c4ae1 --- /dev/null +++ b/packages/update_checker/lib/src/repositories/firebase_base.dart @@ -0,0 +1,6 @@ +/// Firebase base repository will help to fetch the update information from +/// the firebase system. +abstract class FireBaseBase { + /// Fetches the update information from the firebase system. + Future fetchUpdateData(); +} diff --git a/packages/update_checker/lib/src/repositories/firebase_database.dart b/packages/update_checker/lib/src/repositories/firebase_database.dart index 2838984b4..d4e82e478 100644 --- a/packages/update_checker/lib/src/repositories/firebase_database.dart +++ b/packages/update_checker/lib/src/repositories/firebase_database.dart @@ -1,14 +1,16 @@ import 'dart:io'; import 'package:firebase_database/firebase_database.dart'; +import 'package:update_checker/src/repositories/firebase_base.dart'; /// Firebase Database repository will help to fetch the update information from /// the firebase database. -class FirebaseDatabaseRepository { +class FirebaseDatabaseRepository implements FireBaseBase{ /// Initializes the Firebase Database repository const FirebaseDatabaseRepository(); /// Fetches the update information from the database. + @override Future fetchUpdateData() async { final DatabaseReference dbRef = FirebaseDatabase.instance .ref() diff --git a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart new file mode 100644 index 000000000..38dc092f7 --- /dev/null +++ b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart @@ -0,0 +1,19 @@ +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:update_checker/src/repositories/firebase_base.dart'; + +/// Firebase Remote Config repository will help to fetch the update information from +/// the firebase database. +class FirebaseRemoteConfigRepository implements FireBaseBase { + /// Initializes the Firebase Database repository + const FirebaseRemoteConfigRepository(); + + static const String _versionControlKey = 'p2p_in_app_dialog'; + + /// Fetches the update information from the database. + @override + Future fetchUpdateData() async { + final RemoteConfigValue remoteConfigValue = + FirebaseRemoteConfig.instance.getValue(_versionControlKey); + return remoteConfigValue.asString(); + } +} diff --git a/packages/update_checker/lib/src/repositories/repositories.dart b/packages/update_checker/lib/src/repositories/repositories.dart index 5c648fda1..58e2dc7f6 100644 --- a/packages/update_checker/lib/src/repositories/repositories.dart +++ b/packages/update_checker/lib/src/repositories/repositories.dart @@ -1,2 +1,3 @@ export 'firebase_database.dart'; export 'package_info.dart'; +export 'firebase_remote_config.dart'; diff --git a/packages/update_checker/pubspec.lock b/packages/update_checker/pubspec.lock index 0d7ce4b5e..6a9f8499d 100644 --- a/packages/update_checker/pubspec.lock +++ b/packages/update_checker/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: f175bc1414e4edf8c5b83372c98eeabecf8353f39c9da423c2cfdf1f1f508788 + sha256: eb0ac20f704799b986049fbb3c1c16421eca319a1b872378d669513e12452ba5 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.3.14" analyzer: dependency: transitive description: @@ -85,10 +85,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" convert: dependency: transitive description: @@ -157,50 +157,74 @@ packages: dependency: transitive description: name: firebase_core - sha256: ed611fb8e67e43ecc7956f242cecca383d87cf71aace27287aa5dd4bdba4ac07 + sha256: d301561d614487688d797717bef013a264c517d1d09e4c5c1325c3a64c835efb url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.24.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: "0df0a064ab0cad7f8836291ca6f3272edd7b83ad5b3540478ee46a0849d8022b" + sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 url: "https://pub.dev" source: hosted - version: "4.6.0" + version: "5.0.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "347351a8f0518f3343d79a9a0690fa67ad232fc32e2ea270677791949eac792b" + sha256: "10159d9ee42c79f4548971d92f3f0fcd5791f6738cda3583a4e3b2c8b244c018" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.9.0" firebase_database: dependency: "direct main" description: name: firebase_database - sha256: d437ccb3537485cbcf92df60c493b1601e93bce579518a9874c8d7be5408231e + sha256: "373b54cc0a890f4f1547380d31232a718aa5e4ded1a8819e6c4709d7df8286e6" url: "https://pub.dev" source: hosted - version: "10.1.0" + version: "10.3.6" firebase_database_platform_interface: dependency: transitive description: name: firebase_database_platform_interface - sha256: "509351811777e8bcd643f295ae096d8e7741d9bf845467d3626e086d1ac89848" + sha256: "2e3edb4552848585aa0031c0d493699305b40da756ebe507686ca6bbe96369eb" url: "https://pub.dev" source: hosted - version: "0.2.3" + version: "0.2.5+14" firebase_database_web: dependency: transitive description: name: firebase_database_web - sha256: d4a319e7923380f6adc7a46de331ce372aeafe101ae0ec684fef0540d41821bb + sha256: "55ec085db984291668c232d8760b82c86c33f5e4a459721a2df0438c2ece5859" + url: "https://pub.dev" + source: hosted + version: "0.2.3+14" + firebase_remote_config: + dependency: "direct main" + description: + name: firebase_remote_config + sha256: "5266a5ba1fed89517b2e9db3728decb3ff3a1a695d417449381fa9a2a404850c" + url: "https://pub.dev" + source: hosted + version: "4.3.6" + firebase_remote_config_platform_interface: + dependency: transitive + description: + name: firebase_remote_config_platform_interface + sha256: "28b531ade258dbb6084fcf4905cef7f0e9c83574cb18e5010809a7016e157990" url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "1.4.14" + firebase_remote_config_web: + dependency: transitive + description: + name: firebase_remote_config_web + sha256: f11eaf48a081df69b753dafb969b0548d37881d4c4548dea3d12624cb338f44e + url: "https://pub.dev" + source: hosted + version: "1.4.14" flutter: dependency: "direct main" description: flutter @@ -292,18 +316,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -465,10 +489,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -505,26 +529,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" url: "https://pub.dev" source: hosted - version: "1.24.1" + version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" test_core: dependency: transitive description: name: test_core - sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.5.3" typed_data: dependency: transitive description: @@ -557,6 +581,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -590,5 +622,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.10.2" diff --git a/packages/update_checker/pubspec.yaml b/packages/update_checker/pubspec.yaml index 9577e8206..e5cdbb83f 100644 --- a/packages/update_checker/pubspec.yaml +++ b/packages/update_checker/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: sdk: flutter flutter_bloc: ^8.1.2 package_info_plus: ^3.0.3 + firebase_remote_config: ^4.3.0 dev_dependencies: bloc_test: ^9.1.1 diff --git a/packages/update_checker/test/update_checker_test.dart b/packages/update_checker/test/update_checker_test.dart index 4df3734c6..0a55db9df 100644 --- a/packages/update_checker/test/update_checker_test.dart +++ b/packages/update_checker/test/update_checker_test.dart @@ -38,7 +38,7 @@ void main() { when(() => mockPackageInfoRepository.getAppBuildNumber()) .thenAnswer((_) async => buildNumber); return UpdateBloc( - firebaseDatabaseRepository: mockFirebaseDatabaseRepository, + fireBaseRepository: mockFirebaseDatabaseRepository, packageInfoRepository: mockPackageInfoRepository, ); }, @@ -48,7 +48,10 @@ void main() { test( 'should emit UpdateInitialState as initial state', - () => expect(UpdateBloc().state, UpdateInitialState()), + () => expect( + UpdateBloc(fireBaseRepository: mockFirebaseDatabaseRepository) + .state, + UpdateInitialState()), ); generateBlocTest( From dfae26b4455990388eeaf0d8bd636540f111f2bf Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Mon, 4 Dec 2023 16:49:05 +0800 Subject: [PATCH 46/63] update key --- .../lib/src/repositories/firebase_remote_config.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart index 38dc092f7..2cf767e90 100644 --- a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart +++ b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart @@ -7,7 +7,7 @@ class FirebaseRemoteConfigRepository implements FireBaseBase { /// Initializes the Firebase Database repository const FirebaseRemoteConfigRepository(); - static const String _versionControlKey = 'p2p_in_app_dialog'; + static const String _versionControlKey = 'app_version_control'; /// Fetches the update information from the database. @override From e4de232f7d2a8ccee803b48a8e11a8fdf0ac5231 Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:52:18 +0800 Subject: [PATCH 47/63] update version rename base firebase class checking platform type --- .../lib/src/bloc/update_bloc.dart | 28 ++++++++++++++----- .../lib/src/presentation/update_checker.dart | 4 +-- ...{firebase_base.dart => base_firebase.dart} | 2 +- .../src/repositories/firebase_database.dart | 4 +-- .../repositories/firebase_remote_config.dart | 4 +-- packages/update_checker/pubspec.yaml | 2 +- 6 files changed, 29 insertions(+), 15 deletions(-) rename packages/update_checker/lib/src/repositories/{firebase_base.dart => base_firebase.dart} (87%) diff --git a/packages/update_checker/lib/src/bloc/update_bloc.dart b/packages/update_checker/lib/src/bloc/update_bloc.dart index 940db01c6..44e6aaf02 100644 --- a/packages/update_checker/lib/src/bloc/update_bloc.dart +++ b/packages/update_checker/lib/src/bloc/update_bloc.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:update_checker/src/repositories/firebase_base.dart'; +import 'package:update_checker/src/repositories/base_firebase.dart'; import '../models/models.dart'; import '../repositories/repositories.dart'; @@ -30,7 +30,7 @@ class UpdateBloc extends Bloc { } /// Firebase database repository for fetching the update information. - final FireBaseBase fireBaseRepository; + final BaseFireBase fireBaseRepository; /// Package info repository for fetching the app build number. final PackageInfoRepository packageInfoRepository; @@ -51,6 +51,16 @@ class UpdateBloc extends Bloc { } } + String? _platformName() { + if (Platform.isAndroid) { + return 'android'; + } else if (Platform.isIOS) { + return 'ios'; + } else { + return null; + } + } + Future _getUpdateInfoFromRemoteConfig() async { final String rawData = await fireBaseRepository.fetchUpdateData(); @@ -67,10 +77,14 @@ class UpdateBloc extends Bloc { final Map mapValues = json.decode(rawData); - final num minVersion = - mapValues[Platform.isAndroid ? 'android' : 'ios']['version']['minimum']; - final num latestVersion = - mapValues[Platform.isAndroid ? 'android' : 'ios']['version']['latest']; + final String? platformName = _platformName(); + + if (platformName == null) { + return null; + } + + final num minVersion = mapValues[platformName]['version']['minimum']; + final num latestVersion = mapValues[platformName]['version']['latest']; final bool isMandatory = appBuildNumber < minVersion; @@ -83,7 +97,7 @@ class UpdateBloc extends Bloc { } return _createUpdate( - mapValues[Platform.isAndroid ? 'android' : 'ios'], + mapValues[platformName], isOptional, isOptional ? latestVersion : minVersion, ); diff --git a/packages/update_checker/lib/src/presentation/update_checker.dart b/packages/update_checker/lib/src/presentation/update_checker.dart index 337e73686..9af9c336c 100644 --- a/packages/update_checker/lib/src/presentation/update_checker.dart +++ b/packages/update_checker/lib/src/presentation/update_checker.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:update_checker/src/repositories/firebase_base.dart'; +import 'package:update_checker/src/repositories/base_firebase.dart'; import '../../update_checker.dart'; @@ -22,7 +22,7 @@ class UpdateChecker extends StatefulWidget { /// The [fireBaseRepository] that fetch the update information /// from the firebase system. - final FireBaseBase fireBaseRepository; + final BaseFireBase fireBaseRepository; /// [onAvailable] will be called when there is an update available. final Function(UpdateInfo)? onAvailable; diff --git a/packages/update_checker/lib/src/repositories/firebase_base.dart b/packages/update_checker/lib/src/repositories/base_firebase.dart similarity index 87% rename from packages/update_checker/lib/src/repositories/firebase_base.dart rename to packages/update_checker/lib/src/repositories/base_firebase.dart index c186c4ae1..42aef0540 100644 --- a/packages/update_checker/lib/src/repositories/firebase_base.dart +++ b/packages/update_checker/lib/src/repositories/base_firebase.dart @@ -1,6 +1,6 @@ /// Firebase base repository will help to fetch the update information from /// the firebase system. -abstract class FireBaseBase { +abstract class BaseFireBase { /// Fetches the update information from the firebase system. Future fetchUpdateData(); } diff --git a/packages/update_checker/lib/src/repositories/firebase_database.dart b/packages/update_checker/lib/src/repositories/firebase_database.dart index d4e82e478..4d56296f5 100644 --- a/packages/update_checker/lib/src/repositories/firebase_database.dart +++ b/packages/update_checker/lib/src/repositories/firebase_database.dart @@ -1,11 +1,11 @@ import 'dart:io'; import 'package:firebase_database/firebase_database.dart'; -import 'package:update_checker/src/repositories/firebase_base.dart'; +import 'package:update_checker/src/repositories/base_firebase.dart'; /// Firebase Database repository will help to fetch the update information from /// the firebase database. -class FirebaseDatabaseRepository implements FireBaseBase{ +class FirebaseDatabaseRepository implements BaseFireBase{ /// Initializes the Firebase Database repository const FirebaseDatabaseRepository(); diff --git a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart index 2cf767e90..be9de4a58 100644 --- a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart +++ b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart @@ -1,9 +1,9 @@ import 'package:firebase_remote_config/firebase_remote_config.dart'; -import 'package:update_checker/src/repositories/firebase_base.dart'; +import 'package:update_checker/src/repositories/base_firebase.dart'; /// Firebase Remote Config repository will help to fetch the update information from /// the firebase database. -class FirebaseRemoteConfigRepository implements FireBaseBase { +class FirebaseRemoteConfigRepository implements BaseFireBase { /// Initializes the Firebase Database repository const FirebaseRemoteConfigRepository(); diff --git a/packages/update_checker/pubspec.yaml b/packages/update_checker/pubspec.yaml index e5cdbb83f..acae41d42 100644 --- a/packages/update_checker/pubspec.yaml +++ b/packages/update_checker/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: sdk: flutter flutter_bloc: ^8.1.2 package_info_plus: ^3.0.3 - firebase_remote_config: ^4.3.0 + firebase_remote_config: ^4.3.6 dev_dependencies: bloc_test: ^9.1.1 From 294a70e362042b1fa148b5dcdd948006e9b2f457 Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:20:47 +0800 Subject: [PATCH 48/63] fixing decode changelogs data. --- packages/update_checker/lib/src/bloc/update_bloc.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/update_checker/lib/src/bloc/update_bloc.dart b/packages/update_checker/lib/src/bloc/update_bloc.dart index 44e6aaf02..5e66a534b 100644 --- a/packages/update_checker/lib/src/bloc/update_bloc.dart +++ b/packages/update_checker/lib/src/bloc/update_bloc.dart @@ -143,7 +143,11 @@ class UpdateBloc extends Bloc { final String? rawChangelogs = rawUpdateInfo['changelogs']?.toString(); final Map? changelogs = rawChangelogs != null ? json.decode( - rawChangelogs.toString().substring(1, rawChangelogs.length - 1), + fireBaseRepository is FirebaseRemoteConfigRepository + ? rawChangelogs + : rawChangelogs + .toString() + .substring(1, rawChangelogs.length - 1), ) : null; From 1da410ea67d0926894a9cd820f7e21bdb980cbe0 Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:36:43 +0800 Subject: [PATCH 49/63] fixing change logs issue --- .../lib/src/bloc/update_bloc.dart | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/update_checker/lib/src/bloc/update_bloc.dart b/packages/update_checker/lib/src/bloc/update_bloc.dart index 5e66a534b..b557f4ec3 100644 --- a/packages/update_checker/lib/src/bloc/update_bloc.dart +++ b/packages/update_checker/lib/src/bloc/update_bloc.dart @@ -140,16 +140,17 @@ class UpdateBloc extends Bloc { bool isOptional, num buildNumber, ) { - final String? rawChangelogs = rawUpdateInfo['changelogs']?.toString(); - final Map? changelogs = rawChangelogs != null - ? json.decode( - fireBaseRepository is FirebaseRemoteConfigRepository - ? rawChangelogs - : rawChangelogs - .toString() - .substring(1, rawChangelogs.length - 1), - ) - : null; + final Map? changelogs; + if (fireBaseRepository is FirebaseRemoteConfigRepository) { + changelogs = rawUpdateInfo['changelogs']; + } else { + final String? rawChangelogs = rawUpdateInfo['changelogs']?.toString(); + changelogs = rawChangelogs != null + ? json.decode( + rawChangelogs.toString().substring(1, rawChangelogs.length - 1), + ) + : null; + } return UpdateInfo( buildNumber: buildNumber, From fe575595700fc8a9d4303a5829ab3347d60a52a7 Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:51:03 +0800 Subject: [PATCH 50/63] fixing change logs issue --- .../lib/src/repositories/firebase_remote_config.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart index be9de4a58..28d1fd945 100644 --- a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart +++ b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart @@ -12,6 +12,10 @@ class FirebaseRemoteConfigRepository implements BaseFireBase { /// Fetches the update information from the database. @override Future fetchUpdateData() async { + await FirebaseRemoteConfig.instance.app + .setAutomaticDataCollectionEnabled(true); + FirebaseRemoteConfig.instance.settings.minimumFetchInterval = + const Duration(seconds: 10); final RemoteConfigValue remoteConfigValue = FirebaseRemoteConfig.instance.getValue(_versionControlKey); return remoteConfigValue.asString(); From dd72ef861122331ed0c98514a9bcf6e0d9a98a22 Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:03:39 +0800 Subject: [PATCH 51/63] add fetchAndActivate to remote config --- .../lib/src/repositories/firebase_remote_config.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart index 28d1fd945..b8d6d872d 100644 --- a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart +++ b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart @@ -14,8 +14,9 @@ class FirebaseRemoteConfigRepository implements BaseFireBase { Future fetchUpdateData() async { await FirebaseRemoteConfig.instance.app .setAutomaticDataCollectionEnabled(true); - FirebaseRemoteConfig.instance.settings.minimumFetchInterval = - const Duration(seconds: 10); + FirebaseRemoteConfig.instance.settings.fetchTimeout = + const Duration(seconds: 1); + await FirebaseRemoteConfig.instance.fetchAndActivate(); final RemoteConfigValue remoteConfigValue = FirebaseRemoteConfig.instance.getValue(_versionControlKey); return remoteConfigValue.asString(); From e236fcff69af086b802344b052bb8b3c4992760a Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:06:22 +0800 Subject: [PATCH 52/63] add fetchAndActivate to remote config --- .../lib/src/repositories/firebase_remote_config.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart index b8d6d872d..5bb8e40a8 100644 --- a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart +++ b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart @@ -14,8 +14,9 @@ class FirebaseRemoteConfigRepository implements BaseFireBase { Future fetchUpdateData() async { await FirebaseRemoteConfig.instance.app .setAutomaticDataCollectionEnabled(true); - FirebaseRemoteConfig.instance.settings.fetchTimeout = - const Duration(seconds: 1); + await FirebaseRemoteConfig.instance.setConfigSettings(RemoteConfigSettings( + minimumFetchInterval: const Duration(seconds: 1), + fetchTimeout: const Duration(seconds: 60))); await FirebaseRemoteConfig.instance.fetchAndActivate(); final RemoteConfigValue remoteConfigValue = FirebaseRemoteConfig.instance.getValue(_versionControlKey); From 67d0d1856eb19f05381af21aed2f6d15894b2bfe Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Wed, 6 Dec 2023 23:20:28 +0800 Subject: [PATCH 53/63] add fetchAndActivate to remote config --- .../lib/src/repositories/firebase_remote_config.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart index 5bb8e40a8..c31c94b11 100644 --- a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart +++ b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart @@ -12,11 +12,6 @@ class FirebaseRemoteConfigRepository implements BaseFireBase { /// Fetches the update information from the database. @override Future fetchUpdateData() async { - await FirebaseRemoteConfig.instance.app - .setAutomaticDataCollectionEnabled(true); - await FirebaseRemoteConfig.instance.setConfigSettings(RemoteConfigSettings( - minimumFetchInterval: const Duration(seconds: 1), - fetchTimeout: const Duration(seconds: 60))); await FirebaseRemoteConfig.instance.fetchAndActivate(); final RemoteConfigValue remoteConfigValue = FirebaseRemoteConfig.instance.getValue(_versionControlKey); From 475986d6f9cefd4f73369f90ef4b51cd46396e12 Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:27:55 +0800 Subject: [PATCH 54/63] add huaweiUrl to the UpdateInfo model to redirecting users to app gallery. --- packages/update_checker/lib/src/bloc/update_bloc.dart | 1 + packages/update_checker/lib/src/models/update_info.dart | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/packages/update_checker/lib/src/bloc/update_bloc.dart b/packages/update_checker/lib/src/bloc/update_bloc.dart index b557f4ec3..33ab956d1 100644 --- a/packages/update_checker/lib/src/bloc/update_bloc.dart +++ b/packages/update_checker/lib/src/bloc/update_bloc.dart @@ -155,6 +155,7 @@ class UpdateBloc extends Bloc { return UpdateInfo( buildNumber: buildNumber, url: rawUpdateInfo['url'], + huaweiUrl: rawUpdateInfo['huawei_url'], changelog: decodeBase64(rawUpdateInfo['changelog'] ?? ''), changelogs: changelogs, isOptional: isOptional, diff --git a/packages/update_checker/lib/src/models/update_info.dart b/packages/update_checker/lib/src/models/update_info.dart index 927135868..cd4b6f2b4 100644 --- a/packages/update_checker/lib/src/models/update_info.dart +++ b/packages/update_checker/lib/src/models/update_info.dart @@ -10,6 +10,7 @@ class UpdateInfo extends Equatable { this.changelog, this.changelogs, this.url, + this.huaweiUrl, }); /// [isOptional] determines if the update is optional or not. @@ -33,6 +34,9 @@ class UpdateInfo extends Equatable { /// [url] is alternative url for updating the app, can be empty. final String? url; + /// [huaweiUrl] is Huawei URL for Redirecting Huawei Users to AppGallery. + final String? huaweiUrl; + @override List get props => [isOptional, buildNumber, url]; @@ -41,5 +45,6 @@ class UpdateInfo extends Equatable { 'isOptional: $isOptional, ' 'buildNumber: $buildNumber, ' 'url: $url' + 'huaweiUrl: $huaweiUrl' ')'; } From 0c8b55eba04c86421521f929673cbb3776e5b1b3 Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:25:58 +0800 Subject: [PATCH 55/63] fix comments --- .../example/lib/update_bloc_page.dart | 2 +- .../lib/src/bloc/update_bloc.dart | 22 +++++++++---------- .../lib/src/presentation/update_checker.dart | 4 ++-- .../lib/src/repositories/base_firebase.dart | 2 +- .../src/repositories/firebase_database.dart | 2 +- .../repositories/firebase_remote_config.dart | 2 +- .../test/update_checker_test.dart | 4 ++-- 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/packages/update_checker/example/lib/update_bloc_page.dart b/packages/update_checker/example/lib/update_bloc_page.dart index 771661416..55b0b079f 100644 --- a/packages/update_checker/example/lib/update_bloc_page.dart +++ b/packages/update_checker/example/lib/update_bloc_page.dart @@ -10,7 +10,7 @@ class UpdateBlocPage extends StatefulWidget { class _UpdateBlocPageState extends State { final UpdateBloc updateBloc = - UpdateBloc(fireBaseRepository: const FirebaseRemoteConfigRepository()); + UpdateBloc(firebaseRepository: const FirebaseRemoteConfigRepository()); @override Widget build(BuildContext context) => Scaffold( diff --git a/packages/update_checker/lib/src/bloc/update_bloc.dart b/packages/update_checker/lib/src/bloc/update_bloc.dart index 33ab956d1..4c1865523 100644 --- a/packages/update_checker/lib/src/bloc/update_bloc.dart +++ b/packages/update_checker/lib/src/bloc/update_bloc.dart @@ -20,7 +20,7 @@ class UpdateBloc extends Bloc { /// availability from the Firebase Database and `PackageInfoRepository` to /// check the app version with the update to determine the update availability UpdateBloc({ - required this.fireBaseRepository, + required this.firebaseRepository, this.packageInfoRepository = const PackageInfoRepository(), }) : super(UpdateInitialState()) { on( @@ -30,7 +30,7 @@ class UpdateBloc extends Bloc { } /// Firebase database repository for fetching the update information. - final BaseFireBase fireBaseRepository; + final BaseFirebase firebaseRepository; /// Package info repository for fetching the app build number. final PackageInfoRepository packageInfoRepository; @@ -40,9 +40,9 @@ class UpdateBloc extends Bloc { if (state is! UpdateInProgressState) { emit(UpdateInProgressState()); final UpdateInfo? info = - (fireBaseRepository is FirebaseRemoteConfigRepository) + (firebaseRepository is FirebaseRemoteConfigRepository) ? await _getUpdateInfoFromRemoteConfig() - : await _getUpdateInfoFromDataBase(); + : await _getUpdateInfoFromDatabase(); if (info != null) { emit(UpdateAvailableState(info)); } else { @@ -52,17 +52,15 @@ class UpdateBloc extends Bloc { } String? _platformName() { - if (Platform.isAndroid) { - return 'android'; - } else if (Platform.isIOS) { - return 'ios'; + if (Platform.isAndroid || Platform.isIOS) { + return Platform.operatingSystem; } else { return null; } } Future _getUpdateInfoFromRemoteConfig() async { - final String rawData = await fireBaseRepository.fetchUpdateData(); + final String rawData = await firebaseRepository.fetchUpdateData(); // checks if failed to fetch data from Firebase Database and return if (rawData.isEmpty) { @@ -103,8 +101,8 @@ class UpdateBloc extends Bloc { ); } - Future _getUpdateInfoFromDataBase() async { - final dynamic rawData = await fireBaseRepository.fetchUpdateData(); + Future _getUpdateInfoFromDatabase() async { + final dynamic rawData = await firebaseRepository.fetchUpdateData(); // checks if failed to fetch data from Firebase Database and return if (rawData == null) { @@ -141,7 +139,7 @@ class UpdateBloc extends Bloc { num buildNumber, ) { final Map? changelogs; - if (fireBaseRepository is FirebaseRemoteConfigRepository) { + if (firebaseRepository is FirebaseRemoteConfigRepository) { changelogs = rawUpdateInfo['changelogs']; } else { final String? rawChangelogs = rawUpdateInfo['changelogs']?.toString(); diff --git a/packages/update_checker/lib/src/presentation/update_checker.dart b/packages/update_checker/lib/src/presentation/update_checker.dart index 9af9c336c..5faa3a5cf 100644 --- a/packages/update_checker/lib/src/presentation/update_checker.dart +++ b/packages/update_checker/lib/src/presentation/update_checker.dart @@ -22,7 +22,7 @@ class UpdateChecker extends StatefulWidget { /// The [fireBaseRepository] that fetch the update information /// from the firebase system. - final BaseFireBase fireBaseRepository; + final BaseFirebase fireBaseRepository; /// [onAvailable] will be called when there is an update available. final Function(UpdateInfo)? onAvailable; @@ -43,7 +43,7 @@ class _UpdateCheckerState extends State { @override void initState() { super.initState(); - _updateBloc = UpdateBloc(fireBaseRepository: widget.fireBaseRepository); + _updateBloc = UpdateBloc(firebaseRepository: widget.fireBaseRepository); _updateBloc.add(UpdateFetchEvent()); } diff --git a/packages/update_checker/lib/src/repositories/base_firebase.dart b/packages/update_checker/lib/src/repositories/base_firebase.dart index 42aef0540..cca5598f7 100644 --- a/packages/update_checker/lib/src/repositories/base_firebase.dart +++ b/packages/update_checker/lib/src/repositories/base_firebase.dart @@ -1,6 +1,6 @@ /// Firebase base repository will help to fetch the update information from /// the firebase system. -abstract class BaseFireBase { +abstract class BaseFirebase { /// Fetches the update information from the firebase system. Future fetchUpdateData(); } diff --git a/packages/update_checker/lib/src/repositories/firebase_database.dart b/packages/update_checker/lib/src/repositories/firebase_database.dart index 4d56296f5..67e42aa93 100644 --- a/packages/update_checker/lib/src/repositories/firebase_database.dart +++ b/packages/update_checker/lib/src/repositories/firebase_database.dart @@ -5,7 +5,7 @@ import 'package:update_checker/src/repositories/base_firebase.dart'; /// Firebase Database repository will help to fetch the update information from /// the firebase database. -class FirebaseDatabaseRepository implements BaseFireBase{ +class FirebaseDatabaseRepository implements BaseFirebase{ /// Initializes the Firebase Database repository const FirebaseDatabaseRepository(); diff --git a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart index c31c94b11..8fdd42e2d 100644 --- a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart +++ b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart @@ -3,7 +3,7 @@ import 'package:update_checker/src/repositories/base_firebase.dart'; /// Firebase Remote Config repository will help to fetch the update information from /// the firebase database. -class FirebaseRemoteConfigRepository implements BaseFireBase { +class FirebaseRemoteConfigRepository implements BaseFirebase { /// Initializes the Firebase Database repository const FirebaseRemoteConfigRepository(); diff --git a/packages/update_checker/test/update_checker_test.dart b/packages/update_checker/test/update_checker_test.dart index 0a55db9df..c6b7ba7c0 100644 --- a/packages/update_checker/test/update_checker_test.dart +++ b/packages/update_checker/test/update_checker_test.dart @@ -38,7 +38,7 @@ void main() { when(() => mockPackageInfoRepository.getAppBuildNumber()) .thenAnswer((_) async => buildNumber); return UpdateBloc( - fireBaseRepository: mockFirebaseDatabaseRepository, + firebaseRepository: mockFirebaseDatabaseRepository, packageInfoRepository: mockPackageInfoRepository, ); }, @@ -49,7 +49,7 @@ void main() { test( 'should emit UpdateInitialState as initial state', () => expect( - UpdateBloc(fireBaseRepository: mockFirebaseDatabaseRepository) + UpdateBloc(firebaseRepository: mockFirebaseDatabaseRepository) .state, UpdateInitialState()), ); From 9597bc85960fdc89a2cd5ea645f7a8a063b6ab79 Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:28:49 +0800 Subject: [PATCH 56/63] fix comments --- .../update_checker/example/lib/update_checker_page.dart | 2 +- .../lib/src/presentation/update_checker.dart | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/update_checker/example/lib/update_checker_page.dart b/packages/update_checker/example/lib/update_checker_page.dart index 484e6fe61..1d54f4043 100644 --- a/packages/update_checker/example/lib/update_checker_page.dart +++ b/packages/update_checker/example/lib/update_checker_page.dart @@ -10,7 +10,7 @@ class UpdateCheckerPage extends StatelessWidget { ), body: Builder( builder: (BuildContext context) => UpdateChecker( - fireBaseRepository: const FirebaseRemoteConfigRepository(), + firebaseRepository: const FirebaseRemoteConfigRepository(), onNotAvailable: () => _showSnackBar( context, 'Update not available', diff --git a/packages/update_checker/lib/src/presentation/update_checker.dart b/packages/update_checker/lib/src/presentation/update_checker.dart index 5faa3a5cf..88eeaa134 100644 --- a/packages/update_checker/lib/src/presentation/update_checker.dart +++ b/packages/update_checker/lib/src/presentation/update_checker.dart @@ -10,7 +10,7 @@ class UpdateChecker extends StatefulWidget { /// Checks for update availability as soon as renders and will call the proper /// callback based on the UpdateState. const UpdateChecker({ - required this.fireBaseRepository, + required this.firebaseRepository, this.child, this.onAvailable, this.onNotAvailable, @@ -20,9 +20,9 @@ class UpdateChecker extends StatefulWidget { /// The [child] that UpdateChecker widget will wrap. final Widget? child; - /// The [fireBaseRepository] that fetch the update information + /// The [firebaseRepository] that fetch the update information /// from the firebase system. - final BaseFirebase fireBaseRepository; + final BaseFirebase firebaseRepository; /// [onAvailable] will be called when there is an update available. final Function(UpdateInfo)? onAvailable; @@ -43,7 +43,7 @@ class _UpdateCheckerState extends State { @override void initState() { super.initState(); - _updateBloc = UpdateBloc(firebaseRepository: widget.fireBaseRepository); + _updateBloc = UpdateBloc(firebaseRepository: widget.firebaseRepository); _updateBloc.add(UpdateFetchEvent()); } From 0ef894cf99c49004b1852f21030d13c192493a37 Mon Sep 17 00:00:00 2001 From: Reza <94842463+Reza-deriv@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:31:22 +0800 Subject: [PATCH 57/63] chore:bum up version (#351) --- packages/update_checker/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/update_checker/pubspec.yaml b/packages/update_checker/pubspec.yaml index acae41d42..383a18a47 100644 --- a/packages/update_checker/pubspec.yaml +++ b/packages/update_checker/pubspec.yaml @@ -1,6 +1,6 @@ name: update_checker description: Check and retrieve update information from the server for the given package. -version: 1.1.0 +version: 1.2.0 homepage: https://deriv.com/ publish_to: "none" From fd1d8ed345d4ecf91c5f6c1463c5196b40abcbf6 Mon Sep 17 00:00:00 2001 From: Bassam El Obeid <127500305+bassam-deriv@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:02:03 +0400 Subject: [PATCH 58/63] refactor(analytics): [MOBC-546] Creating unified analytics package. (#315) * Refactoring `analytics` package. * PR review comments on `analytics` package * added `setUserProperty` function to `DerivFirebaseAnalytics` * fixed a test case * EOF `.env.example` in `analytics` package * chore(analytics): fixed package version. * chore(analytics): added `logEvent` function to DerivDatadog * docs(analytics): corrected `setup` function call for datadog * chore(analytics): added `.env` file to `.gitignore` * refactor(analytics): added `debugEnabled` to `RudderstackConfiguration` * docs(analytics): added debugEnabled to RudderstackConfiguration --- packages/analytics/CHANGELOG.md | 19 + packages/analytics/README.md | 200 ++++--- packages/analytics/example/.env.example | 9 + packages/analytics/example/.gitignore | 48 ++ packages/analytics/example/.metadata | 10 + packages/analytics/example/README.md | 9 + .../analytics/example/analysis_options.yaml | 29 + packages/analytics/example/android/.gitignore | 13 + .../example/android/app/build.gradle | 68 +++ .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 34 ++ .../com/example/example/MainActivity.kt | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + .../analytics/example/android/build.gradle | 31 + .../example/android/gradle.properties | 3 + .../analytics/example/android/settings.gradle | 11 + packages/analytics/example/ios/.gitignore | 34 ++ .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + packages/analytics/example/ios/Podfile | 50 ++ packages/analytics/example/ios/Podfile.lock | 178 ++++++ .../ios/Runner.xcodeproj/project.pbxproj | 552 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 87 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 ++++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 ++ .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../analytics/example/ios/Runner/Info.plist | 51 ++ .../ios/Runner/Runner-Bridging-Header.h | 1 + packages/analytics/example/lib/main.dart | 83 +++ .../example/lib/pages/first_page.dart | 28 + .../example/lib/pages/rudderstack.dart | 175 ++++++ .../example/lib/pages/second_page.dart | 54 ++ .../example/lib/pages/splash_screen.dart | 36 ++ packages/analytics/example/pubspec.yaml | 38 ++ packages/analytics/lib/analytics.dart | 84 +-- packages/analytics/lib/core/logger.dart | 15 + .../analytics/lib/sdk/base_analytics.dart | 11 + .../lib/sdk/base_analytics_configuration.dart | 2 + .../datadog/core/datadog_configuration.dart | 45 ++ .../analytics/lib/sdk/datadog/core/enums.dart | 108 ++++ .../lib/sdk/datadog/sdk/deriv_datadog.dart | 109 ++++ .../firebase/core/firebase_configuration.dart | 12 + .../sdk/deriv_firebase_analytics.dart | 105 ++++ .../core/rudderstack_configuration.dart | 20 + .../sdk/deriv_rudderstack_sdk.dart | 109 ++++ packages/analytics/pubspec.yaml | 16 +- .../deriv_datadog_configuration_test.dart | 31 + .../test/datadog/core/enums_test.dart | 27 + .../test/datadog/sdk/deriv_datadog_test.dart | 47 ++ ...firebase_analytics_configuration_test.dart | 20 + .../firebase/sdk/deriv_firebase_sdk_test.dart | 107 ++++ .../deriv_rudderstack_configuration_test.dart | 18 + .../sdk/deriv_rudderstack_sdk_test.dart | 142 +++++ 90 files changed, 3119 insertions(+), 145 deletions(-) create mode 100644 packages/analytics/example/.env.example create mode 100644 packages/analytics/example/.gitignore create mode 100644 packages/analytics/example/.metadata create mode 100644 packages/analytics/example/README.md create mode 100644 packages/analytics/example/analysis_options.yaml create mode 100644 packages/analytics/example/android/.gitignore create mode 100644 packages/analytics/example/android/app/build.gradle create mode 100644 packages/analytics/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/analytics/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/analytics/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt create mode 100644 packages/analytics/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 packages/analytics/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/analytics/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/analytics/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/analytics/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/analytics/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/analytics/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/analytics/example/android/app/src/main/res/values-night/styles.xml create mode 100644 packages/analytics/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/analytics/example/android/app/src/profile/AndroidManifest.xml create mode 100644 packages/analytics/example/android/build.gradle create mode 100644 packages/analytics/example/android/gradle.properties create mode 100644 packages/analytics/example/android/settings.gradle create mode 100644 packages/analytics/example/ios/.gitignore create mode 100644 packages/analytics/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/analytics/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/analytics/example/ios/Flutter/Release.xcconfig create mode 100644 packages/analytics/example/ios/Podfile create mode 100644 packages/analytics/example/ios/Podfile.lock create mode 100644 packages/analytics/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/analytics/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/analytics/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/analytics/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/analytics/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/analytics/example/ios/Runner/AppDelegate.swift create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 packages/analytics/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/analytics/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/analytics/example/ios/Runner/Info.plist create mode 100644 packages/analytics/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 packages/analytics/example/lib/main.dart create mode 100644 packages/analytics/example/lib/pages/first_page.dart create mode 100644 packages/analytics/example/lib/pages/rudderstack.dart create mode 100644 packages/analytics/example/lib/pages/second_page.dart create mode 100644 packages/analytics/example/lib/pages/splash_screen.dart create mode 100644 packages/analytics/example/pubspec.yaml create mode 100644 packages/analytics/lib/core/logger.dart create mode 100644 packages/analytics/lib/sdk/base_analytics.dart create mode 100644 packages/analytics/lib/sdk/base_analytics_configuration.dart create mode 100644 packages/analytics/lib/sdk/datadog/core/datadog_configuration.dart create mode 100644 packages/analytics/lib/sdk/datadog/core/enums.dart create mode 100644 packages/analytics/lib/sdk/datadog/sdk/deriv_datadog.dart create mode 100644 packages/analytics/lib/sdk/firebase/core/firebase_configuration.dart create mode 100644 packages/analytics/lib/sdk/firebase/sdk/deriv_firebase_analytics.dart create mode 100644 packages/analytics/lib/sdk/rudderstack/core/rudderstack_configuration.dart create mode 100644 packages/analytics/lib/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart create mode 100644 packages/analytics/test/datadog/core/deriv_datadog_configuration_test.dart create mode 100644 packages/analytics/test/datadog/core/enums_test.dart create mode 100644 packages/analytics/test/datadog/sdk/deriv_datadog_test.dart create mode 100644 packages/analytics/test/firebase/core/deriv_firebase_analytics_configuration_test.dart create mode 100644 packages/analytics/test/firebase/sdk/deriv_firebase_sdk_test.dart create mode 100644 packages/analytics/test/rudderstack/core/deriv_rudderstack_configuration_test.dart create mode 100644 packages/analytics/test/rudderstack/sdk/deriv_rudderstack_sdk_test.dart diff --git a/packages/analytics/CHANGELOG.md b/packages/analytics/CHANGELOG.md index 0a228ceff..896b85d94 100644 --- a/packages/analytics/CHANGELOG.md +++ b/packages/analytics/CHANGELOG.md @@ -1,3 +1,22 @@ +## [2.0.0] +- Introduced a flexible configuration system to support multiple analytics providers. +- Added `BaseAnalyticsConfiguration` interface for standardizing analytics configuration. +- Internalized and restructured Datadog analytics by providing a `DerivDatadog` class that implements `BaseAnalytics`. +- Refined existing analytics services to have their own classes implementing `BaseAnalytics`. +- Introduced Enum types and extensions (`TrackingConsent`, `DatadogSite`) to aid in analytics configuration. +- Improved navigation observer support to work with multiple analytics providers. +- Enhanced exception handling to capture Flutter and Platform-specific errors. +- Added ability to set user information for analytics tracking. +- Replaced in-house Rudderstack plugin with the official Rudderstack SDK. + +### Breaking changes: +**General**: +- `setup()` method for analytics providers now requires configuration classes implementing `BaseAnalyticsConfiguration`. +- Exception handling updated to include multiple analytics providers. +- Rudderstack: Now requires the official Rudderstack SDK for analytics. Update dependencies accordingly. +- Datadog: Migrated from a separate package to an internal implementation within this package. +- Existing analytics services have been restructured to implement `BaseAnalytics`, potentially changing function signatures and configurations. + ## [1.0.0] - Migrated the package to null safety. diff --git a/packages/analytics/README.md b/packages/analytics/README.md index 1b38b96a5..a2d25a263 100644 --- a/packages/analytics/README.md +++ b/packages/analytics/README.md @@ -1,121 +1,127 @@ -# analytics -*** -This package is used for collecting and sending analytical information from the app to "Firebase" and "RudderStack". +# Deriv Analytics Library + +A Flutter analytics library that integrates multiple analytics services including Datadog, Firebase, and RudderStack. + +## Table of Contents + +1. [Installation](#installation) +2. [Quick Start](#quick-start) +3. [Configuration](#configuration) + - [Datadog](#datadog) + - [Firebase](#firebase) + - [RudderStack](#rudderstack) +4. [Logging Events](#logging-events) +5. [User Identification](#user-identification) +6. [Tracking Screens](#tracking-screens) + ## Installation -##### 1. Add to pubspec.yaml + +To install the package, add the following to your `pubspec.yaml`: + ```yaml -analytics: +dependencies: + analytics: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/analytics ref: ``` -Setup the Android and iOS sources as described at rudderstack.com and generate the write keys for Android and iOS sources. - -Set the WRITE_KEY in addition to enabling the plugin to track app lifecycle, screens, and whether to enable debug mode or not in Android and iOS as follows: - -### Android -```xml - - - - [...] - - - - - - - - - -``` -### iOS -```xml - - - - - [...] - com.deriv.rudderstack.WRITE_KEY - ADD-YOUR-KEY - com.deriv.rudderstack.TRACK_APPLICATION_LIFECYCLE_EVENTS - - com.deriv.rudderstack.RECORD_SCREEN_VIEWS - - com.deriv.rudderstack.DEBUG - - [...] - - -``` +Run `flutter packages get` to fetch the package. + +## Quick Start + +Initialize the analytics services you want to use in your `main.dart`: -## How to use -*** -##### 1. Enabling analytics. -```dart -Analytics().init( - isEnabled: true, // set value to false for disable 'Analytics' - ); -``` -##### 2. To track PageRoute transitions. -```dart -MaterialApp( - navigatorObservers: Analytics().observer == null - ? [] - : [Analytics().observer], - ); -``` -##### 3. Logging to 'RudderStack' in different scenarios. -###### when app is opened. -```dart -Analytics().logAppOpened(); -``` -###### when app is in background. ```dart -Analytics().logAppBackgrounded(); +final datadog = DerivDatadog(); +final firebase = DerivFirebaseAnalytics(); +final rudderstack = DerivRudderstack(); + +void main() { + + WidgetsFlutterBinding.ensureInitialized(); + + DerivDatadogConfiguration configuration = DerivDatadogConfiguration( + applicationId: 'DATADOG_APPLICATION_ID', + clientToken: 'DATADOG_CLIENT_TOKEN', + env: 'DATADOG_ENVIRONMENT', + serviceName: 'DATADOG_SERVICE_NAME', + trackingConsent: TrackingConsent.granted, + ); + + DerivDatadog().setup(configuration); + + DerivRudderstack().setup(RudderstackConfiguration( + dataPlaneUrl: 'RUDDERSTACK_DATA_PLANE_URL', + writeKey: 'RUDDERSTACK_WRITE_KEY', + debugEnabled: true, + )); + + DerivFirebaseAnalytics(FirebaseAnalytics.instanceFor(app: await Firebase.initializeApp())).setup( + FirebaseConfiguration( + isAnalyticsCollectionEnabled: true, + ), + ); + + runApp(MyApp()); +} ``` -###### when app is crashed. + +## Configuration + +### Datadog + ```dart -Analytics().logAppCrashed(); +await datadog.setup(DerivDatadogConfiguration( + clientToken: 'your_client_token', + applicationId: 'your_application_id', + env: 'production', + trackingConsent: TrackingConsent.granted, +)); ``` -##### 4. Sending information about current screen. +### Firebase + ```dart -Analytics().setCurrentScreen(screenName: ""); +await firebase.setup(FirebaseConfiguration( + isAnalyticsCollectionEnabled: true, +)); ``` -##### 4. Setting routes/screens which need to be ignored for analytics. + +### RudderStack + ```dart -Analytics().setIgnoredRoutes([ - 'IGNORED_SCREEN_NAME_1', - 'IGNORED_SCREEN_NAME_2', - '.....................', - 'IGNORED_SCREEN_NAME_N' - ]); +await rudderstack.setup(RudderstackConfiguration( + dataPlaneUrl: 'your_data_plane_url', + writeKey: 'your_write_key', +)); ``` -##### 4. Sending information during user login. + +## Logging Events + +You can log events as follows: + ```dart -Analytics().logLoginEvent(userId: "",); +datadog.logEvent('button_click', {'label': 'cta_button'}); +firebase.logEvent('button_click', {'label': 'cta_button'}); +rudderstack.track('button_click'); ``` -##### 7. Sending information during user logout. + +## User Identification + +To identify a user: + ```dart -Analytics().logLogoutEvent(); +datadog.setUserInfo(id: '123', email: 'email@example.com'); +firebase.setUserId(id: '123'); +rudderstack.identify(userId: '123'); ``` -##### 8. Sending information about important events to "Firebase". + +## Tracking Screens + ```dart -Analytics().logToFirebase( - name: "", - params: {'PARAM_1': 'VALUE_1', - 'PARAM_1': 'VALUE_1', - '.......': '.......', - 'PARAM_N': 'VALUE_N'}, - ); +datadog.screen(screenName: 'Home'); +firebase.setCurrentScreen(screenName: 'Home'); +rudderstack.screen(screenName: 'Home'); ``` diff --git a/packages/analytics/example/.env.example b/packages/analytics/example/.env.example new file mode 100644 index 000000000..ccbb3537c --- /dev/null +++ b/packages/analytics/example/.env.example @@ -0,0 +1,9 @@ +DATADOG_APPLICATION_ID = +DATADOG_CLIENT_TOKEN = +DATADOG_ENVIRONMENT = +DATADOG_SERVICE_NAME = + + + +RUDDERSTACK_DATA_PLANE_URL = +RUDDERSTACK_WRITE_KEY = diff --git a/packages/analytics/example/.gitignore b/packages/analytics/example/.gitignore new file mode 100644 index 000000000..a62ba7b84 --- /dev/null +++ b/packages/analytics/example/.gitignore @@ -0,0 +1,48 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +.env \ No newline at end of file diff --git a/packages/analytics/example/.metadata b/packages/analytics/example/.metadata new file mode 100644 index 000000000..3c3e4b52f --- /dev/null +++ b/packages/analytics/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 5464c5bac742001448fe4fc0597be939379f88ea + channel: stable + +project_type: app diff --git a/packages/analytics/example/README.md b/packages/analytics/example/README.md new file mode 100644 index 000000000..6b3bf8eb5 --- /dev/null +++ b/packages/analytics/example/README.md @@ -0,0 +1,9 @@ +# Analytics Example App + +## How to run + +1. Clone this repository +2. Run `cp .env.example .env` +3. Add your RudderStack and Datadog configurations to `.env` +4. Run `flutter pub get` +5. Run `flutter run` \ No newline at end of file diff --git a/packages/analytics/example/analysis_options.yaml b/packages/analytics/example/analysis_options.yaml new file mode 100644 index 000000000..61b6c4de1 --- /dev/null +++ b/packages/analytics/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/analytics/example/android/.gitignore b/packages/analytics/example/android/.gitignore new file mode 100644 index 000000000..6f568019d --- /dev/null +++ b/packages/analytics/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/packages/analytics/example/android/app/build.gradle b/packages/analytics/example/android/app/build.gradle new file mode 100644 index 000000000..806b0464e --- /dev/null +++ b/packages/analytics/example/android/app/build.gradle @@ -0,0 +1,68 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 21 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.example" + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32:$kotlin_version" +} diff --git a/packages/analytics/example/android/app/src/debug/AndroidManifest.xml b/packages/analytics/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..c208884f3 --- /dev/null +++ b/packages/analytics/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/packages/analytics/example/android/app/src/main/AndroidManifest.xml b/packages/analytics/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..3f41384db --- /dev/null +++ b/packages/analytics/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/packages/analytics/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/packages/analytics/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt new file mode 100644 index 000000000..e793a000d --- /dev/null +++ b/packages/analytics/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/packages/analytics/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/analytics/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000..f74085f3f --- /dev/null +++ b/packages/analytics/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/analytics/example/android/app/src/main/res/drawable/launch_background.xml b/packages/analytics/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000..304732f88 --- /dev/null +++ b/packages/analytics/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/analytics/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/analytics/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/analytics/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/analytics/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/analytics/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/analytics/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/analytics/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/analytics/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/analytics/example/android/app/src/main/res/values-night/styles.xml b/packages/analytics/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..3db14bb53 --- /dev/null +++ b/packages/analytics/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/analytics/example/android/app/src/main/res/values/styles.xml b/packages/analytics/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..d460d1e92 --- /dev/null +++ b/packages/analytics/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/analytics/example/android/app/src/profile/AndroidManifest.xml b/packages/analytics/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..c208884f3 --- /dev/null +++ b/packages/analytics/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/packages/analytics/example/android/build.gradle b/packages/analytics/example/android/build.gradle new file mode 100644 index 000000000..aa282b69a --- /dev/null +++ b/packages/analytics/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.0' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/analytics/example/android/gradle.properties b/packages/analytics/example/android/gradle.properties new file mode 100644 index 000000000..94adc3a3f --- /dev/null +++ b/packages/analytics/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/analytics/example/android/settings.gradle b/packages/analytics/example/android/settings.gradle new file mode 100644 index 000000000..44e62bcf0 --- /dev/null +++ b/packages/analytics/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/packages/analytics/example/ios/.gitignore b/packages/analytics/example/ios/.gitignore new file mode 100644 index 000000000..7a7f9873a --- /dev/null +++ b/packages/analytics/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/packages/analytics/example/ios/Flutter/AppFrameworkInfo.plist b/packages/analytics/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000..9625e105d --- /dev/null +++ b/packages/analytics/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 11.0 + + diff --git a/packages/analytics/example/ios/Flutter/Debug.xcconfig b/packages/analytics/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000..ec97fc6f3 --- /dev/null +++ b/packages/analytics/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/analytics/example/ios/Flutter/Release.xcconfig b/packages/analytics/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000..c4855bfe2 --- /dev/null +++ b/packages/analytics/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/analytics/example/ios/Podfile b/packages/analytics/example/ios/Podfile new file mode 100644 index 000000000..68df00b22 --- /dev/null +++ b/packages/analytics/example/ios/Podfile @@ -0,0 +1,50 @@ +# Uncomment this line to define a global platform for your project +platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +# post_install do |installer| +# installer.pods_project.targets.each do |target| +# flutter_additional_ios_build_settings(target) +# end +# end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' + end + end + end diff --git a/packages/analytics/example/ios/Podfile.lock b/packages/analytics/example/ios/Podfile.lock new file mode 100644 index 000000000..4be589ed5 --- /dev/null +++ b/packages/analytics/example/ios/Podfile.lock @@ -0,0 +1,178 @@ +PODS: + - datadog_flutter_plugin (0.0.1): + - DatadogSDK (= 1.23.0) + - DatadogSDKCrashReporting (= 1.23.0) + - DictionaryCoder (= 1.0.8) + - Flutter + - DatadogSDK (1.23.0) + - DatadogSDKCrashReporting (1.23.0): + - DatadogSDK (= 1.23.0) + - PLCrashReporter (~> 1.11.0) + - DictionaryCoder (1.0.8) + - Firebase/Analytics (10.15.0): + - Firebase/Core + - Firebase/Core (10.15.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 10.15.0) + - Firebase/CoreOnly (10.15.0): + - FirebaseCore (= 10.15.0) + - firebase_analytics (10.6.1): + - Firebase/Analytics (= 10.15.0) + - firebase_core + - Flutter + - firebase_core (2.19.0): + - Firebase/CoreOnly (= 10.15.0) + - Flutter + - FirebaseAnalytics (10.15.0): + - FirebaseAnalytics/AdIdSupport (= 10.15.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseAnalytics/AdIdSupport (10.15.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleAppMeasurement (= 10.15.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseCore (10.15.0): + - FirebaseCoreInternal (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/Logger (~> 7.8) + - FirebaseCoreInternal (10.16.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseInstallations (10.16.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) + - PromisesObjC (~> 2.1) + - Flutter (1.0.0) + - GoogleAppMeasurement (10.15.0): + - GoogleAppMeasurement/AdIdSupport (= 10.15.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (10.15.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.15.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleAppMeasurement/WithoutAdIdSupport (10.15.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleUtilities/AppDelegateSwizzler (7.11.5): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.11.5): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.11.5): + - GoogleUtilities/Environment + - GoogleUtilities/MethodSwizzler (7.11.5): + - GoogleUtilities/Logger + - GoogleUtilities/Network (7.11.5): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.11.5)" + - GoogleUtilities/Reachability (7.11.5): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (7.11.5): + - GoogleUtilities/Logger + - MetricsReporter (1.1.1): + - RSCrashReporter (= 1.0.0) + - RudderKit (= 1.4.0) + - nanopb (2.30909.0): + - nanopb/decode (= 2.30909.0) + - nanopb/encode (= 2.30909.0) + - nanopb/decode (2.30909.0) + - nanopb/encode (2.30909.0) + - PLCrashReporter (1.11.1) + - PromisesObjC (2.3.1) + - RSCrashReporter (1.0.0) + - Rudder (1.23.0): + - MetricsReporter (= 1.1.1) + - rudder_plugin_ios (0.0.1): + - Flutter + - Rudder (< 2.0.0, >= 1.23.0) + - RudderKit (1.4.0) + +DEPENDENCIES: + - datadog_flutter_plugin (from `.symlinks/plugins/datadog_flutter_plugin/ios`) + - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - Flutter (from `Flutter`) + - rudder_plugin_ios (from `.symlinks/plugins/rudder_plugin_ios/ios`) + +SPEC REPOS: + trunk: + - DatadogSDK + - DatadogSDKCrashReporting + - DictionaryCoder + - Firebase + - FirebaseAnalytics + - FirebaseCore + - FirebaseCoreInternal + - FirebaseInstallations + - GoogleAppMeasurement + - GoogleUtilities + - MetricsReporter + - nanopb + - PLCrashReporter + - PromisesObjC + - RSCrashReporter + - Rudder + - RudderKit + +EXTERNAL SOURCES: + datadog_flutter_plugin: + :path: ".symlinks/plugins/datadog_flutter_plugin/ios" + firebase_analytics: + :path: ".symlinks/plugins/firebase_analytics/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + Flutter: + :path: Flutter + rudder_plugin_ios: + :path: ".symlinks/plugins/rudder_plugin_ios/ios" + +SPEC CHECKSUMS: + datadog_flutter_plugin: 8f47e24d1953bd48658d44bb557e173534f26ac2 + DatadogSDK: c2519eea53cc46c17d28a90b9784c3b265afdb52 + DatadogSDKCrashReporting: c3d443bad91788a9c4f781e079fa0a43564bedd7 + DictionaryCoder: 5f84fff69f54cb806071538430bdafe04a89d658 + Firebase: 66043bd4579e5b73811f96829c694c7af8d67435 + firebase_analytics: 8b63b894ac3be5ced6a9a4614174754b03377c2f + firebase_core: fd674fcc642742ef7289acea60bd21a1a021bd98 + FirebaseAnalytics: 47cef43728f81a839cf1306576bdd77ffa2eac7e + FirebaseCore: 2cec518b43635f96afe7ac3a9c513e47558abd2e + FirebaseCoreInternal: 26233f705cc4531236818a07ac84d20c333e505a + FirebaseInstallations: b822f91a61f7d1ba763e5ccc9d4f2e6f2ed3b3ee + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + GoogleAppMeasurement: 722db6550d1e6d552b08398b69a975ac61039338 + GoogleUtilities: 13e2c67ede716b8741c7989e26893d151b2b2084 + MetricsReporter: 759631361ffd2b8f0d375b1225c8a631311f6da2 + nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 + PLCrashReporter: 5d2d3967afe0efad61b3048d617e2199a5d1b787 + PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 + RSCrashReporter: e9ccaebd996263f8325a6cbef44ff74aa5f58e30 + Rudder: 125d9dc03e178b35b0f74487aa694afe1a8e6f4f + rudder_plugin_ios: 940ac533f6b3882d7d4389d084aeb2a844c8f4c4 + RudderKit: f272f9872183946452ac94cd7bb2244a71e6ca8f + +PODFILE CHECKSUM: 9eb4a36c9b6b15b2354428bb0f3d2873ffa3b2e6 + +COCOAPODS: 1.13.0 diff --git a/packages/analytics/example/ios/Runner.xcodeproj/project.pbxproj b/packages/analytics/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..86dcea23b --- /dev/null +++ b/packages/analytics/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,552 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 083EBA9E56DE58145841DF61 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52E312214333A7F75C8F00F6 /* Pods_Runner.framework */; }; + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 482409292D5D6DE86C778A03 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 52E312214333A7F75C8F00F6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 83DB042B106A298E00E72DAC /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9995D6A89D80469EC59877B9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 083EBA9E56DE58145841DF61 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + C6A8115C1B6376D40F79AA1D /* Pods */, + ADB2A804837615BFCC32E1C6 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + ADB2A804837615BFCC32E1C6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 52E312214333A7F75C8F00F6 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + C6A8115C1B6376D40F79AA1D /* Pods */ = { + isa = PBXGroup; + children = ( + 83DB042B106A298E00E72DAC /* Pods-Runner.debug.xcconfig */, + 482409292D5D6DE86C778A03 /* Pods-Runner.release.xcconfig */, + 9995D6A89D80469EC59877B9 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + F034C2F5ACDA6DC94A5B86B5 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 5538329A9D37E08C0AD65335 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1430; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 5538329A9D37E08C0AD65335 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + F034C2F5ACDA6DC94A5B86B5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/packages/analytics/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/analytics/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/analytics/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..a6b826db2 --- /dev/null +++ b/packages/analytics/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/analytics/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/analytics/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..21a3cc14c --- /dev/null +++ b/packages/analytics/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/analytics/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/analytics/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/analytics/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/analytics/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/analytics/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/packages/analytics/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/analytics/example/ios/Runner/AppDelegate.swift b/packages/analytics/example/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000..70693e4a8 --- /dev/null +++ b/packages/analytics/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..d36b1fab2 --- /dev/null +++ b/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 zcmeHN2~<R zh`|8`A_PQ1nSu(UMFx?8j8PC!!VDphaL#`F42fd#7Vlc`zIE4n%Y~eiz4y1j|NDpi z?<@|pSJ-HM`qifhf@m%MamgwK83`XpBA<+azdF#2QsT{X@z0A9Bq>~TVErigKH1~P zRX-!h-f0NJ4Mh++{D}J+K>~~rq}d%o%+4dogzXp7RxX4C>Km5XEI|PAFDmo;DFm6G zzjVoB`@qW98Yl0Kvc-9w09^PrsobmG*Eju^=3f?0o-t$U)TL1B3;sZ^!++3&bGZ!o-*6w?;oOhf z=A+Qb$scV5!RbG+&2S}BQ6YH!FKb0``VVX~T$dzzeSZ$&9=X$3)_7Z{SspSYJ!lGE z7yig_41zpQ)%5dr4ff0rh$@ky3-JLRk&DK)NEIHecf9c*?Z1bUB4%pZjQ7hD!A0r-@NF(^WKdr(LXj|=UE7?gBYGgGQV zidf2`ZT@pzXf7}!NH4q(0IMcxsUGDih(0{kRSez&z?CFA0RVXsVFw3^u=^KMtt95q z43q$b*6#uQDLoiCAF_{RFc{!H^moH_cmll#Fc^KXi{9GDl{>%+3qyfOE5;Zq|6#Hb zp^#1G+z^AXfRKaa9HK;%b3Ux~U@q?xg<2DXP%6k!3E)PA<#4$ui8eDy5|9hA5&{?v z(-;*1%(1~-NTQ`Is1_MGdQ{+i*ccd96ab$R$T3=% zw_KuNF@vI!A>>Y_2pl9L{9h1-C6H8<)J4gKI6{WzGBi<@u3P6hNsXG=bRq5c+z;Gc3VUCe;LIIFDmQAGy+=mRyF++u=drBWV8-^>0yE9N&*05XHZpPlE zxu@?8(ZNy7rm?|<+UNe0Vs6&o?l`Pt>P&WaL~M&#Eh%`rg@Mbb)J&@DA-wheQ>hRV z<(XhigZAT z>=M;URcdCaiO3d^?H<^EiEMDV+7HsTiOhoaMX%P65E<(5xMPJKxf!0u>U~uVqnPN7T!X!o@_gs3Ct1 zlZ_$5QXP4{Aj645wG_SNT&6m|O6~Tsl$q?nK*)(`{J4b=(yb^nOATtF1_aS978$x3 zx>Q@s4i3~IT*+l{@dx~Hst21fR*+5}S1@cf>&8*uLw-0^zK(+OpW?cS-YG1QBZ5q! zgTAgivzoF#`cSz&HL>Ti!!v#?36I1*l^mkrx7Y|K6L#n!-~5=d3;K<;Zqi|gpNUn_ z_^GaQDEQ*jfzh;`j&KXb66fWEk1K7vxQIMQ_#Wu_%3 z4Oeb7FJ`8I>Px;^S?)}2+4D_83gHEq>8qSQY0PVP?o)zAv3K~;R$fnwTmI-=ZLK`= zTm+0h*e+Yfr(IlH3i7gUclNH^!MU>id$Jw>O?2i0Cila#v|twub21@e{S2v}8Z13( zNDrTXZVgris|qYm<0NU(tAPouG!QF4ZNpZPkX~{tVf8xY690JqY1NVdiTtW+NqyRP zZ&;T0ikb8V{wxmFhlLTQ&?OP7 z;(z*<+?J2~z*6asSe7h`$8~Se(@t(#%?BGLVs$p``;CyvcT?7Y!{tIPva$LxCQ&4W z6v#F*);|RXvI%qnoOY&i4S*EL&h%hP3O zLsrFZhv&Hu5tF$Lx!8(hs&?!Kx5&L(fdu}UI5d*wn~A`nPUhG&Rv z2#ixiJdhSF-K2tpVL=)5UkXRuPAFrEW}7mW=uAmtVQ&pGE-&az6@#-(Te^n*lrH^m@X-ftVcwO_#7{WI)5v(?>uC9GG{lcGXYJ~Q8q zbMFl7;t+kV;|;KkBW2!P_o%Czhw&Q(nXlxK9ak&6r5t_KH8#1Mr-*0}2h8R9XNkr zto5-b7P_auqTJb(TJlmJ9xreA=6d=d)CVbYP-r4$hDn5|TIhB>SReMfh&OVLkMk-T zYf%$taLF0OqYF?V{+6Xkn>iX@TuqQ?&cN6UjC9YF&%q{Ut3zv{U2)~$>-3;Dp)*(? zg*$mu8^i=-e#acaj*T$pNowo{xiGEk$%DusaQiS!KjJH96XZ-hXv+jk%ard#fu=@Q z$AM)YWvE^{%tDfK%nD49=PI|wYu}lYVbB#a7wtN^Nml@CE@{Gv7+jo{_V?I*jkdLD zJE|jfdrmVbkfS>rN*+`#l%ZUi5_bMS<>=MBDNlpiSb_tAF|Zy`K7kcp@|d?yaTmB^ zo?(vg;B$vxS|SszusORgDg-*Uitzdi{dUV+glA~R8V(?`3GZIl^egW{a919!j#>f` znL1o_^-b`}xnU0+~KIFLQ)$Q6#ym%)(GYC`^XM*{g zv3AM5$+TtDRs%`2TyR^$(hqE7Y1b&`Jd6dS6B#hDVbJlUXcG3y*439D8MrK!2D~6gn>UD4Imctb z+IvAt0iaW73Iq$K?4}H`7wq6YkTMm`tcktXgK0lKPmh=>h+l}Y+pDtvHnG>uqBA)l zAH6BV4F}v$(o$8Gfo*PB>IuaY1*^*`OTx4|hM8jZ?B6HY;F6p4{`OcZZ(us-RVwDx zUzJrCQlp@mz1ZFiSZ*$yX3c_#h9J;yBE$2g%xjmGF4ca z&yL`nGVs!Zxsh^j6i%$a*I3ZD2SoNT`{D%mU=LKaEwbN(_J5%i-6Va?@*>=3(dQy` zOv%$_9lcy9+(t>qohkuU4r_P=R^6ME+wFu&LA9tw9RA?azGhjrVJKy&8=*qZT5Dr8g--d+S8zAyJ$1HlW3Olryt`yE zFIph~Z6oF&o64rw{>lgZISC6p^CBer9C5G6yq%?8tC+)7*d+ib^?fU!JRFxynRLEZ zj;?PwtS}Ao#9whV@KEmwQgM0TVP{hs>dg(1*DiMUOKHdQGIqa0`yZnHk9mtbPfoLx zo;^V6pKUJ!5#n`w2D&381#5#_t}AlTGEgDz$^;u;-vxDN?^#5!zN9ngytY@oTv!nc zp1Xn8uR$1Z;7vY`-<*?DfPHB;x|GUi_fI9@I9SVRv1)qETbNU_8{5U|(>Du84qP#7 z*l9Y$SgA&wGbj>R1YeT9vYjZuC@|{rajTL0f%N@>3$DFU=`lSPl=Iv;EjuGjBa$Gw zHD-;%YOE@<-!7-Mn`0WuO3oWuL6tB2cpPw~Nvuj|KM@))ixuDK`9;jGMe2d)7gHin zS<>k@!x;!TJEc#HdL#RF(`|4W+H88d4V%zlh(7#{q2d0OQX9*FW^`^_<3r$kabWAB z$9BONo5}*(%kx zOXi-yM_cmB3>inPpI~)duvZykJ@^^aWzQ=eQ&STUa}2uT@lV&WoRzkUoE`rR0)`=l zFT%f|LA9fCw>`enm$p7W^E@U7RNBtsh{_-7vVz3DtB*y#*~(L9+x9*wn8VjWw|Q~q zKFsj1Yl>;}%MG3=PY`$g$_mnyhuV&~O~u~)968$0b2!Jkd;2MtAP#ZDYw9hmK_+M$ zb3pxyYC&|CuAbtiG8HZjj?MZJBFbt`ryf+c1dXFuC z0*ZQhBzNBd*}s6K_G}(|Z_9NDV162#y%WSNe|FTDDhx)K!c(mMJh@h87@8(^YdK$&d*^WQe8Z53 z(|@MRJ$Lk-&ii74MPIs80WsOFZ(NX23oR-?As+*aq6b?~62@fSVmM-_*cb1RzZ)`5$agEiL`-E9s7{GM2?(KNPgK1(+c*|-FKoy}X(D_b#etO|YR z(BGZ)0Ntfv-7R4GHoXp?l5g#*={S1{u-QzxCGng*oWr~@X-5f~RA14b8~B+pLKvr4 zfgL|7I>jlak9>D4=(i(cqYf7#318!OSR=^`xxvI!bBlS??`xxWeg?+|>MxaIdH1U~#1tHu zB{QMR?EGRmQ_l4p6YXJ{o(hh-7Tdm>TAX380TZZZyVkqHNzjUn*_|cb?T? zt;d2s-?B#Mc>T-gvBmQZx(y_cfkXZO~{N zT6rP7SD6g~n9QJ)8F*8uHxTLCAZ{l1Y&?6v)BOJZ)=R-pY=Y=&1}jE7fQ>USS}xP#exo57uND0i*rEk@$;nLvRB@u~s^dwRf?G?_enN@$t* zbL%JO=rV(3Ju8#GqUpeE3l_Wu1lN9Y{D4uaUe`g>zlj$1ER$6S6@{m1!~V|bYkhZA z%CvrDRTkHuajMU8;&RZ&itnC~iYLW4DVkP<$}>#&(`UO>!n)Po;Mt(SY8Yb`AS9lt znbX^i?Oe9r_o=?})IHKHoQGKXsps_SE{hwrg?6dMI|^+$CeC&z@*LuF+P`7LfZ*yr+KN8B4{Nzv<`A(wyR@!|gw{zB6Ha ziwPAYh)oJ(nlqSknu(8g9N&1hu0$vFK$W#mp%>X~AU1ay+EKWcFdif{% z#4!4aoVVJ;ULmkQf!ke2}3hqxLK>eq|-d7Ly7-J9zMpT`?dxo6HdfJA|t)?qPEVBDv z{y_b?4^|YA4%WW0VZd8C(ZgQzRI5(I^)=Ub`Y#MHc@nv0w-DaJAqsbEHDWG8Ia6ju zo-iyr*sq((gEwCC&^TYBWt4_@|81?=B-?#P6NMff(*^re zYqvDuO`K@`mjm_Jd;mW_tP`3$cS?R$jR1ZN09$YO%_iBqh5ftzSpMQQtxKFU=FYmP zeY^jph+g<4>YO;U^O>-NFLn~-RqlHvnZl2yd2A{Yc1G@Ga$d+Q&(f^tnPf+Z7serIU};17+2DU_f4Z z@GaPFut27d?!YiD+QP@)T=77cR9~MK@bd~pY%X(h%L={{OIb8IQmf-!xmZkm8A0Ga zQSWONI17_ru5wpHg3jI@i9D+_Y|pCqVuHJNdHUauTD=R$JcD2K_liQisqG$(sm=k9;L* z!L?*4B~ql7uioSX$zWJ?;q-SWXRFhz2Jt4%fOHA=Bwf|RzhwqdXGr78y$J)LR7&3T zE1WWz*>GPWKZ0%|@%6=fyx)5rzUpI;bCj>3RKzNG_1w$fIFCZ&UR0(7S?g}`&Pg$M zf`SLsz8wK82Vyj7;RyKmY{a8G{2BHG%w!^T|Njr!h9TO2LaP^_f22Q1=l$QiU84ao zHe_#{S6;qrC6w~7{y(hs-?-j?lbOfgH^E=XcSgnwW*eEz{_Z<_Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 zcmVjJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x literal 0 HcmV?d00001 diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/analytics/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 zcmV-m2cP(fP)x~L`~4d)Rspd&<9kFh{hn*KP1LP0~$;u(LfAu zp%fx&qLBcRHx$G|3q(bv@+b;o0*D|jwD-Q9uQR(l*ST}s+uPgQ-MeFwZ#GS?b332? z&Tk$&_miXn3IGq)AmQ)3sisq{raD4(k*bHvpCe-TdWq^NRTEVM)i9xbgQ&ccnUVx* zEY%vS%gDcSg=!tuIK8$Th2_((_h^+7;R|G{n06&O2#6%LK`a}n?h_fL18btz<@lFG za}xS}u?#DBMB> zw^b($1Z)`9G?eP95EKi&$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD? zmr-s{0wRmxUnbDrYfRvnZ@d z6johZ2sMX{YkGSKWd}m|@V7`Degt-43=2M?+jR%8{(H$&MLLmS;-|JxnX2pnz;el1jsvqQz}pGSF<`mqEXRQ5sC4#BbwnB_4` zc5bFE-Gb#JV3tox9fp-vVEN{(tOCpRse`S+@)?%pz+zVJXSooTrNCUg`R6`hxwb{) zC@{O6MKY8tfZ5@!yy=p5Y|#+myRL=^{tc(6YgAnkg3I(Cd!r5l;|;l-MQ8B`;*SCE z{u)uP^C$lOPM z5d~UhKhRRmvv{LIa^|oavk1$QiEApSrP@~Jjbg`<*dW4TO?4qG%a%sTPUFz(QtW5( zM)lA+5)0TvH~aBaOAs|}?u2FO;yc-CZ1gNM1dAxJ?%m?YsGR`}-xk2*dxC}r5j$d* zE!#Vtbo69h>V4V`BL%_&$} z+oJAo@jQ^Tk`;%xw-4G>hhb&)B?##U+(6Fi7nno`C<|#PVA%$Y{}N-?(Gc$1%tr4Pc}}hm~yY#fTOe!@v9s-ik$dX~|ygArPhByaXn8 zpI^FUjNWMsTFKTP3X7m?UK)3m zp6rI^_zxRYrx6_QmhoWoDR`fp4R7gu6;gdO)!KexaoO2D88F9x#TM1(9Bn7g;|?|o z)~$n&Lh#hCP6_LOPD>a)NmhW})LADx2kq=X7}7wYRj-0?dXr&bHaRWCfSqvzFa=sn z-8^gSyn-RmH=BZ{AJZ~!8n5621GbUJV7Qvs%JNv&$%Q17s_X%s-41vAPfIR>;x0Wlqr5?09S>x#%Qkt>?(&XjFRY}*L6BeQ3 z<6XEBh^S7>AbwGm@XP{RkeEKj6@_o%oV?hDuUpUJ+r#JZO?!IUc;r0R?>mi)*ZpQ) z#((dn=A#i_&EQn|hd)N$#A*fjBFuiHcYvo?@y1 z5|fV=a^a~d!c-%ZbMNqkMKiSzM{Yq=7_c&1H!mXk60Uv32dV;vMg&-kQ)Q{+PFtwc zj|-uQ;b^gts??J*9VxxOro}W~Q9j4Em|zSRv)(WSO9$F$s=Ydu%Q+5DOid~lwk&we zY%W(Z@ofdwPHncEZzZgmqS|!gTj3wQq9rxQy+^eNYKr1mj&?tm@wkO*9@UtnRMG>c aR{jt9+;fr}hV%pg00001^@s67{VYS000c7NklQEG_j zup^)eW&WUIApqy$=APz8jE@awGp)!bsTjDbrJO`$x^ZR^dr;>)LW>{ zs70vpsD38v)19rI=GNk1b(0?Js9~rjsQsu*K;@SD40RB-3^gKU-MYC7G!Bw{fZsqp zih4iIi;Hr_xZ033Iu{sQxLS=}yBXgLMn40d++>aQ0#%8D1EbGZp7+ z5=mK?t31BkVYbGOxE9`i748x`YgCMwL$qMsChbSGSE1`p{nSmadR zcQ#R)(?!~dmtD0+D2!K zR9%!Xp1oOJzm(vbLvT^$IKp@+W2=-}qTzTgVtQ!#Y7Gxz}stUIm<1;oBQ^Sh2X{F4ibaOOx;5ZGSNK z0maF^@(UtV$=p6DXLgRURwF95C=|U8?osGhgOED*b z7woJ_PWXBD>V-NjQAm{~T%sjyJ{5tn2f{G%?J!KRSrrGvQ1(^`YLA5B!~eycY(e5_ z*%aa{at13SxC(=7JT7$IQF~R3sy`Nn%EMv!$-8ZEAryB*yB1k&stni)=)8-ODo41g zkJu~roIgAih94tb=YsL%iH5@^b~kU9M-=aqgXIrbtxMpFy5mekFm#edF9z7RQ6V}R zBIhbXs~pMzt0VWy1Fi$^fh+1xxLDoK09&5&MJl(q#THjPm(0=z2H2Yfm^a&E)V+a5 zbi>08u;bJsDRUKR9(INSc7XyuWv(JsD+BB*0hS)FO&l&7MdViuur@-<-EHw>kHRGY zqoT}3fDv2-m{NhBG8X}+rgOEZ;amh*DqN?jEfQdqxdj08`Sr=C-KmT)qU1 z+9Cl)a1mgXxhQiHVB}l`m;-RpmKy?0*|yl?FXvJkFxuu!fKlcmz$kN(a}i*saM3nr z0!;a~_%Xqy24IxA2rz<+08=B-Q|2PT)O4;EaxP^6qixOv7-cRh?*T?zZU`{nIM-at zTKYWr9rJ=tppQ9I#Z#mLgINVB!pO-^FOcvFw6NhV0gztuO?g ztoA*C-52Q-Z-P#xB4HAY3KQVd%dz1S4PA3vHp0aa=zAO?FCt zC_GaTyVBg2F!bBr3U@Zy2iJgIAt>1sf$JWA9kh{;L+P*HfUBX1Zy{4MgNbDfBV_ly z!y#+753arsZUt@366jIC0klaC@ckuk!qu=pAyf7&QmiBUT^L1&tOHzsK)4n|pmrVT zs2($4=?s~VejTFHbFdDOwG;_58LkIj1Fh@{glkO#F1>a==ymJS$z;gdedT1zPx4Kj ztjS`y_C}%af-RtpehdQDt3a<=W5C4$)9W@QAse;WUry$WYmr51ml9lkeunUrE`-3e zmq1SgSOPNEE-Mf+AGJ$g0M;3@w!$Ej;hMh=v=I+Lpz^n%Pg^MgwyqOkNyu2c^of)C z1~ALor3}}+RiF*K4+4{(1%1j3pif1>sv0r^mTZ?5Jd-It!tfPfiG_p$AY*Vfak%FG z4z#;wLtw&E&?}w+eKG^=#jF7HQzr8rV0mY<1YAJ_uGz~$E13p?F^fPSzXSn$8UcI$ z8er9{5w5iv0qf8%70zV71T1IBB1N}R5Kp%NO0=5wJalZt8;xYp;b{1K) zHY>2wW-`Sl{=NpR%iu3(u6l&)rc%%cSA#aV7WCowfbFR4wcc{LQZv~o1u_`}EJA3>ki`?9CKYTA!rhO)if*zRdd}Kn zEPfYbhoVE~!FI_2YbC5qAj1kq;xP6%J8+?2PAs?`V3}nyFVD#sV3+uP`pi}{$l9U^ zSz}_M9f7RgnnRhaoIJgT8us!1aB&4!*vYF07Hp&}L zCRlop0oK4DL@ISz{2_BPlezc;xj2|I z23RlDNpi9LgTG_#(w%cMaS)%N`e>~1&a3<{Xy}>?WbF>OOLuO+j&hc^YohQ$4F&ze z+hwnro1puQjnKm;vFG~o>`kCeUIlkA-2tI?WBKCFLMBY=J{hpSsQ=PDtU$=duS_hq zHpymHt^uuV1q@uc4bFb{MdG*|VoW@15Osrqt2@8ll0qO=j*uOXn{M0UJX#SUztui9FN4)K3{9!y8PC-AHHvpVTU;x|-7P+taAtyglk#rjlH2 z5Gq8ik}BPaGiM{#Woyg;*&N9R2{J0V+WGB69cEtH7F?U~Kbi6ksi*`CFXsi931q7Y zGO82?whBhN%w1iDetv%~wM*Y;E^)@Vl?VDj-f*RX>{;o_=$fU!&KAXbuadYZ46Zbg z&6jMF=49$uL^73y;;N5jaHYv)BTyfh&`qVLYn?`o6BCA_z-0niZz=qPG!vonK3MW_ zo$V96zM!+kJRs{P-5-rQVse0VBH*n6A58)4uc&gfHMa{gIhV2fGf{st>E8sKyP-$8zp~wJX^A*@DI&-;8>gANXZj zU)R+Y)PB?=)a|Kj>8NXEu^S_h^7R`~Q&7*Kn!xyvzVv&^>?^iu;S~R2e-2fJx-oUb cX)(b1KSk$MOV07*qoM6N<$f&6$jw%VRuvdN2+38CZWny1cRtlsl+0_KtW)EU14Ei(F!UtWuj4IK+3{sK@>rh zs1Z;=(DD&U6+tlyL?UnHVN^&g6QhFi2#HS+*qz;(>63G(`|jRtW|nz$Pv7qTovP!^ zP_jES{mr@O-02w%!^a?^1ZP!_KmQiz0L~jZ=W@Qt`8wzOoclQsAS<5YdH;a(4bGLE zk8s}1If(PSIgVi!XE!5kA?~z*sobvNyohr;=Q_@h2@$6Flyej3J)D-6YfheRGl`HEcPk|~huT_2-U?PfL=4BPV)f1o!%rQ!NMt_MYw-5bUSwQ9Z&zC>u zOrl~UJglJNa%f50Ok}?WB{on`Ci`p^Y!xBA?m@rcJXLxtrE0FhRF3d*ir>yzO|BD$ z3V}HpFcCh6bTzY}Nt_(W%QYd3NG)jJ4<`F<1Od) zfQblTdC&h2lCz`>y?>|9o2CdvC8qZeIZt%jN;B7Hdn2l*k4M4MFEtq`q_#5?}c$b$pf_3y{Y!cRDafZBEj-*OD|gz#PBDeu3QoueOesLzB+O zxjf2wvf6Wwz>@AiOo2mO4=TkAV+g~%_n&R;)l#!cBxjuoD$aS-`IIJv7cdX%2{WT7 zOm%5rs(wqyPE^k5SIpUZ!&Lq4<~%{*>_Hu$2|~Xa;iX*tz8~G6O3uFOS?+)tWtdi| zV2b#;zRN!m@H&jd=!$7YY6_}|=!IU@=SjvGDFtL;aCtw06U;-v^0%k0FOyESt z1Wv$={b_H&8FiRV?MrzoHWd>%v6KTRU;-v^Miiz+@q`(BoT!+<37CKhoKb)|8!+RG z6BQFU^@fRW;s8!mOf2QViKQGk0TVER6EG1`#;Nm39Do^PoT!+<37AD!%oJe86(=et zZ~|sLzU>V-qYiU6V8$0GmU7_K8|Fd0B?+9Un1BhKAz#V~Fk^`mJtlCX#{^8^M8!me z8Yg;8-~>!e<-iG;h*0B1kBKm}hItVGY6WnjVpgnTTAC$rqQ^v)4KvOtpY|sIj@WYg zyw##ZZ5AC2IKNC;^hwg9BPk0wLStlmBr;E|$5GoAo$&Ui_;S9WY62n3)i49|T%C#i017z3J=$RF|KyZWnci*@lW4 z=AKhNN6+m`Q!V3Ye68|8y@%=am>YD0nG99M)NWc20%)gwO!96j7muR}Fr&54SxKP2 zP30S~lt=a*qDlbu3+Av57=9v&vr<6g0&`!8E2fq>I|EJGKs}t|{h7+KT@)LfIV-3K zK)r_fr2?}FFyn*MYoLC>oV-J~eavL2ho4a4^r{E-8m2hi>~hA?_vIG4a*KT;2eyl1 zh_hUvUJpNCFwBvRq5BI*srSle>c6%n`#VNsyC|MGa{(P&08p=C9+WUw9Hl<1o9T4M zdD=_C0F7#o8A_bRR?sFNmU0R6tW`ElnF8p53IdHo#S9(JoZCz}fHwJ6F<&?qrpVqE zte|m%89JQD+XwaPU#%#lVs-@-OL);|MdfINd6!XwP2h(eyafTUsoRkA%&@fe?9m@jw-v(yTTiV2(*fthQH9}SqmsRPVnwwbV$1E(_lkmo&S zF-truCU914_$jpqjr(>Ha4HkM4YMT>m~NosUu&UZ>zirfHo%N6PPs9^_o$WqPA0#5 z%tG>qFCL+b*0s?sZ;Sht0nE7Kl>OVXy=gjWxxK;OJ3yGd7-pZf7JYNcZo2*1SF`u6 zHJyRRxGw9mDlOiXqVMsNe#WX`fC`vrtjSQ%KmLcl(lC>ZOQzG^%iql2w-f_K@r?OE zwCICifM#L-HJyc7Gm>Ern?+Sk3&|Khmu4(~3qa$(m6Ub^U0E5RHq49za|XklN#?kP zl;EstdW?(_4D>kwjWy2f!LM)y?F94kyU3`W!6+AyId-89v}sXJpuic^NLL7GJItl~ zsiuB98AI-(#Mnm|=A-R6&2fwJ0JVSY#Q>&3$zFh|@;#%0qeF=j5Ajq@4i0tIIW z&}sk$&fGwoJpe&u-JeGLi^r?dO`m=y(QO{@h zQqAC7$rvz&5+mo3IqE?h=a~6m>%r5Quapvzq;{y~p zJpyXOBgD9VrW7@#p6l7O?o3feml(DtSL>D^R) zZUY%T2b0-vBAFN7VB;M88!~HuOXi4KcI6aRQ&h|XQ0A?m%j2=l1f0cGP}h(oVfJ`N zz#PpmFC*ieab)zJK<4?^k=g%OjPnkANzbAbmGZHoVRk*mTfm75s_cWVa`l*f$B@xu z5E*?&@seIo#*Y~1rBm!7sF9~~u6Wrj5oICUOuz}CS)jdNIznfzCA(stJ(7$c^e5wN z?lt>eYgbA!kvAR7zYSD&*r1$b|(@;9dcZ^67R0 zXAXJKa|5Sdmj!g578Nwt6d$sXuc&MWezA0Whd`94$h{{?1IwXP4)Tx4obDK%xoFZ_Z zjjHJ_P@R_e5blG@yEjnaJb`l;s%Lb2&=8$&Ct-fV`E^4CUs)=jTk!I}2d&n!f@)bm z@ z_4Dc86+3l2*p|~;o-Sb~oXb_RuLmoifDU^&Te$*FevycC0*nE3Xws8gsWp|Rj2>SM zns)qcYj?^2sd8?N!_w~4v+f-HCF|a$TNZDoNl$I1Uq87euoNgKb6&r26TNrfkUa@o zfdiFA@p{K&mH3b8i!lcoz)V{n8Q@g(vR4ns4r6w;K z>1~ecQR0-<^J|Ndg5fvVUM9g;lbu-){#ghGw(fg>L zh)T5Ljb%lWE;V9L!;Cqk>AV1(rULYF07ZBJbGb9qbSoLAd;in9{)95YqX$J43-dY7YU*k~vrM25 zxh5_IqO0LYZW%oxQ5HOzmk4x{atE*vipUk}sh88$b2tn?!ujEHn`tQLe&vo}nMb&{ zio`xzZ&GG6&ZyN3jnaQy#iVqXE9VT(3tWY$n-)uWDQ|tc{`?fq2F`oQ{;d3aWPg4Hp-(iE{ry>MIPWL> iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000..89c2725b7 --- /dev/null +++ b/packages/analytics/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/analytics/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/analytics/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..f2e259c7c --- /dev/null +++ b/packages/analytics/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/analytics/example/ios/Runner/Base.lproj/Main.storyboard b/packages/analytics/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000..f3c28516f --- /dev/null +++ b/packages/analytics/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/analytics/example/ios/Runner/Info.plist b/packages/analytics/example/ios/Runner/Info.plist new file mode 100644 index 000000000..7f553465b --- /dev/null +++ b/packages/analytics/example/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/packages/analytics/example/ios/Runner/Runner-Bridging-Header.h b/packages/analytics/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000..308a2a560 --- /dev/null +++ b/packages/analytics/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/packages/analytics/example/lib/main.dart b/packages/analytics/example/lib/main.dart new file mode 100644 index 000000000..7412eae85 --- /dev/null +++ b/packages/analytics/example/lib/main.dart @@ -0,0 +1,83 @@ +import 'package:analytics/sdk/datadog/core/datadog_configuration.dart'; +import 'package:analytics/sdk/datadog/core/enums.dart'; +import 'package:analytics/sdk/datadog/sdk/deriv_datadog.dart'; +import 'package:analytics/sdk/rudderstack/core/rudderstack_configuration.dart'; +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; +import 'package:deriv_env/env.dart'; +import 'package:example/pages/rudderstack.dart'; +import 'package:flutter/material.dart'; + + +import 'package:example/pages/first_page.dart'; +import 'package:example/pages/second_page.dart'; +import 'package:example/pages/splash_screen.dart'; + +void main() async { + + WidgetsFlutterBinding.ensureInitialized(); + + await Env().load(); + + DerivDatadogConfiguration configuration = DerivDatadogConfiguration( + applicationId: Env().get( + 'DATADOG_APPLICATION_ID', + ), + clientToken: Env().get( + 'DATADOG_CLIENT_TOKEN', + ), + env: Env().get( + 'DATADOG_ENVIRONMENT', + ), + serviceName: Env().get( + 'DATADOG_SERVICE_NAME', + ), + trackingConsent: TrackingConsent.granted, + ); + + DerivDatadog().setup(configuration); + + DerivRudderstack().setup(RudderstackConfiguration( + dataPlaneUrl: Env().get( + 'RUDDERSTACK_DATA_PLANE_URL' + ), + writeKey: Env().get( + 'RUDDERSTACK_WRITE_KEY', + ), + )); + + runApp(const App()); +} + +class App extends StatefulWidget { + const App({Key? key}) : super(key: key); + + @override + State createState() => _AppState(); +} + +class _AppState extends State { + @override + void initState() { + super.initState(); + + DerivDatadog().setUserInfo( + id: "0", + name: "Example App User", + email: "example_user@deriv.com", + extraInfo: {}, + ); + } + + // This widget is the root of your application. + @override + Widget build(BuildContext context) => MaterialApp( + navigatorObservers: [DerivDatadog().navigatorObserver], + initialRoute: '/splash_screen', + routes: { + '/splash_screen': (context) => const SplashScreen(), + '/': (context) => const FirstPage(), + '/second': (context) => const SecondPage(), + '/rudderstack': (context) => const RudderStack(), + }, + ); +} diff --git a/packages/analytics/example/lib/pages/first_page.dart b/packages/analytics/example/lib/pages/first_page.dart new file mode 100644 index 000000000..9cd47ea61 --- /dev/null +++ b/packages/analytics/example/lib/pages/first_page.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +class FirstPage extends StatefulWidget { + const FirstPage({Key? key}) : super(key: key); + + @override + State createState() => _FirstPageState(); +} + +class _FirstPageState extends State { + @override + Widget build(BuildContext context) => Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("DataDog Test App 1"), + TextButton( + onPressed: () { + Navigator.pushNamed(context, "/second"); + }, + child: const Text("Test Button 1"), + ), + ], + ), + ), + ); +} diff --git a/packages/analytics/example/lib/pages/rudderstack.dart b/packages/analytics/example/lib/pages/rudderstack.dart new file mode 100644 index 000000000..eccb7df3e --- /dev/null +++ b/packages/analytics/example/lib/pages/rudderstack.dart @@ -0,0 +1,175 @@ +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; +import 'package:flutter/material.dart'; + +class RudderStack extends StatefulWidget { + // ignore: public_member_api_docs + const RudderStack({super.key}); + + @override + _RudderStackState createState() => _RudderStackState(); +} + +class _RudderStackState extends State { + bool _enabled = false; + + @override + void initState() { + super.initState(); + + + } + + @override + Widget build(BuildContext context) => MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Rudderstack example app'), + //back button + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context, false), + ), + ), + body: SingleChildScrollView( + child: Builder( + builder: (BuildContext context) => Column( + children: [ + _enableController(context), + const Divider(), + GridView.count( + shrinkWrap: true, + crossAxisCount: 2, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + padding: const EdgeInsets.all(16), + children: _eventsList(context), + ), + ], + ), + ), + ), + ), + ); + + List _eventsList(BuildContext context) => [ + InkWell( + child: Container( + padding: const EdgeInsets.all(8), + color: Colors.teal[100], + child: const Center( + child: Text( + 'Identify', + style: TextStyle(fontSize: 18), + )), + ), + onTap: () async { + await DerivRudderstack() + .setContext(token: 'xxx-xxxx-xxxx-xxxxx-xxxx-test'); + final bool result = + await DerivRudderstack().identify(userId: '998'); + _showSnackBar(context, result); + }, + ), + InkWell( + child: Container( + padding: const EdgeInsets.all(8), + color: Colors.blue[100], + child: const Center( + child: Text( + 'Track', + style: TextStyle(fontSize: 18), + )), + ), + onTap: () async { + final bool result = await DerivRudderstack().track( + eventName: 'Application Opened' + ); + + _showSnackBar(context, result); + }), + InkWell( + child: Container( + padding: const EdgeInsets.all(8), + color: Colors.orange[100], + child: const Center( + child: Text( + 'Screen', + style: TextStyle(fontSize: 18), + )), + ), + onTap: () async { + final bool result = await DerivRudderstack().screen( + screenName: 'main' + ); + + _showSnackBar(context, result); + }), + InkWell( + child: Container( + padding: const EdgeInsets.all(8), + color: Colors.indigo[100], + child: const Center( + child: Text( + 'group', + style: TextStyle(fontSize: 18), + )), + ), + onTap: () async { + final bool result = await DerivRudderstack().group( + groupId: 'Group-id-test' + ); + _showSnackBar(context, result); + }), + InkWell( + child: Container( + padding: const EdgeInsets.all(8), + color: Colors.purple[100], + child: const Center( + child: Text( + 'alias', + style: TextStyle(fontSize: 18), + )), + ), + onTap: () async { + final bool result = + await DerivRudderstack().alias(alias: 'Alias-test'); + _showSnackBar(context, result); + }), + InkWell( + child: Container( + padding: const EdgeInsets.all(8), + color: Colors.deepOrange[100], + child: const Center( + child: Text( + 'reset', + style: TextStyle(fontSize: 18), + )), + ), + onTap: () async { + final bool result = await DerivRudderstack().reset(); + _showSnackBar(context, result); + }), + ]; + + Widget _enableController(BuildContext context) => SwitchListTile( + title: const Text('Enable RudderStack'), + value: _enabled, + onChanged: (bool newValue) async { + setState(() => _enabled = newValue); + + bool result; + if (_enabled) { + result = await DerivRudderstack().enable(); + } else { + result = await DerivRudderstack().disable(); + } + + _showSnackBar(context, result); + }, + ); + + void _showSnackBar(BuildContext context, bool success) => + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(success ? 'Success' : 'Failure')), + ); +} diff --git a/packages/analytics/example/lib/pages/second_page.dart b/packages/analytics/example/lib/pages/second_page.dart new file mode 100644 index 000000000..572587e89 --- /dev/null +++ b/packages/analytics/example/lib/pages/second_page.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; + +class SecondPage extends StatefulWidget { + const SecondPage({Key? key}) : super(key: key); + + @override + State createState() => _SecondPageState(); +} + +class _SecondPageState extends State { + int count = 0; + + @override + Widget build(BuildContext context) => WillPopScope( + onWillPop: () async { + setState(() => count = 0); + + return true; + }, + child: Scaffold( + appBar: AppBar( + elevation: 0, + backgroundColor: Colors.transparent, + iconTheme: const IconThemeData(color: Colors.black), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("DataDog Test App 2"), + if (count > 0 && count < 5) + const Text("This button doesn't do anything."), + if (count >= 5 && count < 10) + const Text( + "STOP CLICKING THE BUTTON IT DOESN'T DO ANYTHING!"), + if (count >= 10) + const Text( + "There is an error! Happy now?!", + style: TextStyle(color: Colors.red), + ), + TextButton( + onPressed: () { + setState(() => count++); + + Navigator.pushNamed(context, "/rudderstack"); + }, + child: const Text("RudderStack"), + ), + ], + ), + ), + ), + ); +} diff --git a/packages/analytics/example/lib/pages/splash_screen.dart b/packages/analytics/example/lib/pages/splash_screen.dart new file mode 100644 index 000000000..9417ad2e5 --- /dev/null +++ b/packages/analytics/example/lib/pages/splash_screen.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +class SplashScreen extends StatefulWidget { + const SplashScreen({Key? key}) : super(key: key); + + @override + State createState() => _SplashScreenState(); +} + +class _SplashScreenState extends State { + @override + void initState() { + super.initState(); + + Future.delayed( + const Duration(seconds: 2), + () => Navigator.pushNamed(context, "/"), + ); + } + + @override + Widget build(BuildContext context) => const Scaffold( + body: SizedBox( + height: double.infinity, + width: double.infinity, + child: Center( + child: Hero( + tag: "datadog_logo", + child: Text( + "Analytics Test App", + ), + ), + ), + ), + ); +} diff --git a/packages/analytics/example/pubspec.yaml b/packages/analytics/example/pubspec.yaml new file mode 100644 index 000000000..98493558a --- /dev/null +++ b/packages/analytics/example/pubspec.yaml @@ -0,0 +1,38 @@ +name: example +description: A new Flutter project. + +publish_to: "none" + +version: 1.0.0+1 + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: "3.10.2" + +dependencies: + flutter: + sdk: flutter + deriv_env: + git: + url: git@github.com:regentmarkets/flutter-deriv-packages.git + path: packages/deriv_env + ref: dev + + analytics: + path: ../ + + cupertino_icons: ^1.0.2 + +dependency_overrides: + + +dev_dependencies: + flutter_test: + sdk: flutter + + flutter_lints: ^1.0.0 + +flutter: + uses-material-design: true + assets: + - .env diff --git a/packages/analytics/lib/analytics.dart b/packages/analytics/lib/analytics.dart index 1aade9933..22574f33a 100644 --- a/packages/analytics/lib/analytics.dart +++ b/packages/analytics/lib/analytics.dart @@ -1,17 +1,21 @@ -import 'dart:io'; - +import 'package:analytics/sdk/firebase/core/firebase_configuration.dart'; +import 'package:analytics/sdk/firebase/sdk/deriv_firebase_analytics.dart'; +import 'package:analytics/sdk/rudderstack/core/rudderstack_configuration.dart'; +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; -import 'package:deriv_rudderstack/deriv_rudderstack.dart'; - import 'analytics_route_observer.dart'; /// Class that collects and send analytical information to `Firebase` and /// `RudderStack`. +@Deprecated( + 'Use DerivFirebaseAnalytics, DerivRudderstack, or DerivDatadog instead.') class Analytics { /// Initialises + @Deprecated( + 'Use DerivFirebaseAnalytics, DerivRudderstack, or DerivDatadog instead.') factory Analytics() => _instance; Analytics._internal(); @@ -20,29 +24,38 @@ class Analytics { static final Analytics _instance = Analytics._internal(); /// Contains ignored routes/screen names. + @Deprecated('No need to have it here as it can be done in the client app.') List ignoredRoutes = []; - late FirebaseAnalytics _firebaseAnalytics; + late DerivFirebaseAnalytics _derivFirebaseAnalytics; /// An instance of custom route observer created for analytics. + @Deprecated( + "Use the 'navigatorObserver' in DerivFirebaseAnalytics, DerivRudderstack, or DerivDatadog instead.") late AnalyticsRouteObserver observer; /// Initialises the `Analytics`. /// Sets the device-token to `RudderStack`. /// bool [isEnabled] enables or disables "Analytics". - Future init({ - required bool isEnabled, - required FirebaseApp firebaseApp, - }) async { - _firebaseAnalytics = FirebaseAnalytics.instanceFor(app: firebaseApp); + @Deprecated( + "Use the 'setup' function in DerivFirebaseAnalytics, DerivRudderstack, or DerivDatadog instead.") + Future init( + {required bool isEnabled, + required FirebaseApp firebaseApp, + RudderstackConfiguration? configuration}) async { + _derivFirebaseAnalytics = + DerivFirebaseAnalytics(FirebaseAnalytics.instanceFor(app: firebaseApp)); observer = AnalyticsRouteObserver(onNewRoute: _newRouteHandler); // Enable or disable the analytics on this device. - await _firebaseAnalytics.setAnalyticsCollectionEnabled(isEnabled); + await _derivFirebaseAnalytics.setup( + FirebaseConfiguration( + isAnalyticsCollectionEnabled: isEnabled, + ), + ); - // For ios we have to manually setup the rudderStack as it's not get initialized with register method. - if (Platform.isIOS) { - await setupRudderStackForIos(); + if (configuration != null) { + await DerivRudderstack().setup(configuration); } isEnabled @@ -52,52 +65,45 @@ class Analytics { /// Captures `screen_view` event on route changes. void _newRouteHandler(PageRoute route) { + // ignore: deprecated_member_use_from_same_package setCurrentScreen( screenName: route.settings.name ?? '', - // ignore: avoid_as - properties: route.settings.arguments as Map? ?? - {}, ); } - /// Captures `app_open` event when the app is opened. - void logAppOpened() { - _firebaseAnalytics.logAppOpen(); - - DerivRudderstack().track(eventName: 'Application Opened'); - } - /// Captures `Application Backgrounded` event when the app goes to background. + @Deprecated("Use the 'track' function in DerivRudderstack instead.") void logAppBackgrounded() { DerivRudderstack().track(eventName: 'Application Backgrounded'); } /// Captures `Application Crashed` event when the app is crashed. + @Deprecated("Use the 'track' function in DerivRudderstack instead.") void logAppCrashed() { DerivRudderstack().track(eventName: 'Application Crashed'); } /// Captures information about current screen in use. + @Deprecated( + "Use the 'setCurrentScreen' function in DerivFirebaseAnalytics or the 'screen' function DerivRudderstack instead.") void setCurrentScreen({ required String screenName, - Map properties = const {}, }) { if (ignoredRoutes.contains(screenName)) { return; } - _firebaseAnalytics.setCurrentScreen(screenName: screenName); + _derivFirebaseAnalytics.setCurrentScreen(screenName: screenName); - DerivRudderstack().screen( - screenName: screenName, - properties: properties, - ); + DerivRudderstack().screen(screenName: screenName); } /// Captures `login` event upon a successful user log in. + @Deprecated( + "Use the 'logLoginEvent' function in DerivFirebaseAnalytics or the 'identify' function in DerivRudderstack instead.") Future logLoginEvent( {required String deviceToken, required int userId}) async { await _setFirebaseUserId(userId.toString()); - await _firebaseAnalytics.logLogin(); + await _derivFirebaseAnalytics.logLogin(); await _setRudderStackDeviceToken(deviceToken); @@ -105,8 +111,10 @@ class Analytics { } /// Captures `logout` event when the user logs out. + @Deprecated( + "Use the 'logLogoutEvent' function in DerivFirebaseAnalytics instead.") void logLogoutEvent() { - _firebaseAnalytics.logEvent(name: 'logout'); + _derivFirebaseAnalytics.logEvent(name: 'logout'); } /// Sets the device-token to `RudderStack`. @@ -115,27 +123,25 @@ class Analytics { /// Sets the user id for `Firebase`. Future _setFirebaseUserId(String userId) => - _firebaseAnalytics.setUserId(id: userId); + _derivFirebaseAnalytics.setUserId(id: userId); /// Logs push token. + @Deprecated("Use the 'setContext' function in DerivRudderstack instead.") Future logPushToken(String deviceToken) async { await _setRudderStackDeviceToken(deviceToken); } - /// This method initialize the rudderStack client for ios. - Future setupRudderStackForIos() async { - await DerivRudderstack().setup(); - } - /// Should be called at logout to clear up current `RudderStack` data. + @Deprecated("Use the 'reset' function in DerivRudderstack instead.") Future reset() async => DerivRudderstack().reset(); /// Logs custom events to `Firebase`. + @Deprecated("Use the 'logEvent' function in DerivFirebaseAnalytics instead.") Future logToFirebase({ required String name, Map? params, }) => - _firebaseAnalytics.logEvent( + _derivFirebaseAnalytics.logEvent( name: name, parameters: params, ); diff --git a/packages/analytics/lib/core/logger.dart b/packages/analytics/lib/core/logger.dart new file mode 100644 index 000000000..20e366259 --- /dev/null +++ b/packages/analytics/lib/core/logger.dart @@ -0,0 +1,15 @@ +import 'dart:developer' as logger_dev; + +/// Logger interface for logging errors or messages +abstract class Logger { + /// Logs a [message]. + void log(String message); +} + +/// Concrete implementation of [Logger]. +class ConsoleLogger implements Logger { + @override + void log(String message) { + logger_dev.log(message); + } +} diff --git a/packages/analytics/lib/sdk/base_analytics.dart b/packages/analytics/lib/sdk/base_analytics.dart new file mode 100644 index 000000000..18041e9ca --- /dev/null +++ b/packages/analytics/lib/sdk/base_analytics.dart @@ -0,0 +1,11 @@ +import 'package:analytics/sdk/base_analytics_configuration.dart'; +import 'package:flutter/widgets.dart'; + +/// Define the analytics interface +abstract class BaseAnalytics { + /// The [NavigatorObserver] instances used for tracking navigation events. + NavigatorObserver get navigatorObserver; + + /// Sets up the analytics client. + Future setup(T configuration); +} diff --git a/packages/analytics/lib/sdk/base_analytics_configuration.dart b/packages/analytics/lib/sdk/base_analytics_configuration.dart new file mode 100644 index 000000000..79438847f --- /dev/null +++ b/packages/analytics/lib/sdk/base_analytics_configuration.dart @@ -0,0 +1,2 @@ +/// Abstract base class that defines the structure for all analytics configurations. +abstract class BaseAnalyticsConfiguration {} diff --git a/packages/analytics/lib/sdk/datadog/core/datadog_configuration.dart b/packages/analytics/lib/sdk/datadog/core/datadog_configuration.dart new file mode 100644 index 000000000..66f5a8d20 --- /dev/null +++ b/packages/analytics/lib/sdk/datadog/core/datadog_configuration.dart @@ -0,0 +1,45 @@ +import 'package:analytics/sdk/base_analytics_configuration.dart'; +import 'package:analytics/sdk/datadog/core/enums.dart'; + +/// A class to define the configuration options for the [DerivDatadog]. +class DerivDatadogConfiguration implements BaseAnalyticsConfiguration { + /// Creates a new [DerivDatadogConfiguration] instance with the given options. + const DerivDatadogConfiguration({ + required this.applicationId, + required this.clientToken, + required this.env, + required this.trackingConsent, + this.site = DatadogSite.us1, + this.nativeCrashReportEnabled = true, + this.sessionSamplingRate = 100, + this.tracingSamplingRate = 100, + this.serviceName, + }); + + /// The application id used to identify the app in the `Datadog` dashboard. + final String applicationId; + + /// The client token used to authenticate with the `Datadog API`. + final String clientToken; + + /// The environment in which the SDK is running. + final String env; + + /// The `Datadog` site to use. + final DatadogSite? site; + + /// The user's tracking consent status. + final TrackingConsent trackingConsent; + + /// Whether native crash reporting is enabled. + final bool? nativeCrashReportEnabled; + + /// The sampling rate for sessions. + final double? sessionSamplingRate; + + /// The sampling rate for resource traces. + final double? tracingSamplingRate; + + /// The service name for this application + final String? serviceName; +} diff --git a/packages/analytics/lib/sdk/datadog/core/enums.dart b/packages/analytics/lib/sdk/datadog/core/enums.dart new file mode 100644 index 000000000..20f4d4e46 --- /dev/null +++ b/packages/analytics/lib/sdk/datadog/core/enums.dart @@ -0,0 +1,108 @@ +import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart' as datadog; + +/// An enum to represent the user's tracking consent status, used in [DatadogSdkConfig]. +enum TrackingConsent { + /// user has granted tracking consent + granted, + + /// user has not granted tracking consent + notGranted, + + /// user's tracking consent is pending + pending, +} + +/// A Extension on [TrackingConsent]. +extension TrackingConsentExtension on TrackingConsent { + /// The consent getter method is an extension to the [datadog.TrackingConsent] + /// enum that returns the corresponding [datadog.TrackingConsent] value for + /// the given [TrackingConsent] value. + datadog.TrackingConsent get consent => trackingConsentMapper[this]!; +} + +/// A mapper class to map [TrackingConsent] to [datadog.TrackingConsent]. +Map trackingConsentMapper = + { + TrackingConsent.granted: datadog.TrackingConsent.granted, + TrackingConsent.notGranted: datadog.TrackingConsent.notGranted, + TrackingConsent.pending: datadog.TrackingConsent.pending, +}; + +/// Determines the server for uploading RUM events. +enum DatadogSite { + /// US based servers. Sends RUM events to + /// [app.datadoghq.com](https://app.datadoghq.com/). + us1, + + /// US based servers. Sends RUM events to + /// [us3.datadoghq.com](https://us3.datadoghq.com/). + us3, + + /// US based servers. Sends RUM events to + /// [us5.datadoghq.com](https://us5.datadoghq.com/). + us5, + + /// Europe based servers. Sends RUM events to + /// [app.datadoghq.eu](https://app.datadoghq.eu/). + eu1, + + /// US based servers, FedRAMP compatible. Sends RUM events to + /// [app.ddog-gov.com](https://app.ddog-gov.com/). + us1Fed, + + /// Asia baesd servers. Sends data to + /// [ap1.datadoghq.com](https://ap1.datadoghq.com). + ap1, +} + +/// A Extension on [DatadogSite]. +extension DatadogSiteExtension on DatadogSite { + /// The consent getter method is an extension to the [datadog.DatadogSite] + /// enum that returns the corresponding [datadog.DatadogSite] value for + /// the given [DatadogSite] value. + datadog.DatadogSite get site => siteMapper[this] ?? datadog.DatadogSite.us1; +} + +/// A mapper class to map [DatadogSite] to [datadog.DatadogSite]. +Map siteMapper = + { + DatadogSite.us1: datadog.DatadogSite.us1, + DatadogSite.us3: datadog.DatadogSite.us3, + DatadogSite.us5: datadog.DatadogSite.us5, + DatadogSite.eu1: datadog.DatadogSite.eu1, + DatadogSite.us1Fed: datadog.DatadogSite.us1Fed, + DatadogSite.ap1: datadog.DatadogSite.ap1, +}; + +/// An enum to represent the user's tracking consent status, used in [DatadogSdkConfig]. +enum RumUserActionType { + /// tap action + tap, + + /// scroll action + scroll, + + /// swipe action + swipe, + + /// custom action + custom +} + +/// A mapper class to map [RumUserActionType] to [datadog.RumUserActionType]. +Map rumUserActionTypeMapper = + { + RumUserActionType.tap: datadog.RumUserActionType.tap, + RumUserActionType.scroll: datadog.RumUserActionType.scroll, + RumUserActionType.swipe: datadog.RumUserActionType.swipe, + RumUserActionType.custom: datadog.RumUserActionType.custom, +}; + +/// Extension on [RumUserActionType]. +extension RumUserActionTypeExtension on RumUserActionType { + /// The consent getter method is an extension to the [datadog.RumUserActionType] + /// enum that returns the corresponding [datadog.RumUserActionType] value for + /// the given [RumUserActionType] value. + datadog.RumUserActionType get rumUserActionType => + rumUserActionTypeMapper[this]!; +} diff --git a/packages/analytics/lib/sdk/datadog/sdk/deriv_datadog.dart b/packages/analytics/lib/sdk/datadog/sdk/deriv_datadog.dart new file mode 100644 index 000000000..e3d91efff --- /dev/null +++ b/packages/analytics/lib/sdk/datadog/sdk/deriv_datadog.dart @@ -0,0 +1,109 @@ +import 'dart:ui'; + +import 'package:analytics/sdk/base_analytics.dart'; +import 'package:analytics/sdk/datadog/core/datadog_configuration.dart'; +import 'package:analytics/sdk/datadog/core/enums.dart'; +import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart' as datadog; +import 'package:datadog_flutter_plugin/datadog_internal.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +/// Implement Datadog +class DerivDatadog implements BaseAnalytics { + /// Returns the singleton instance of the [DerivDatadog]. + factory DerivDatadog() => _instance ??= DerivDatadog._(); + + DerivDatadog._(); + + static DerivDatadog? _instance; + + datadog.DatadogSdk _datadogSDK = datadog.DatadogSdk.instance; + + /// Returns navigation observer for the [Datadog]. + datadog.DatadogNavigationObserver get _navigationObserver => + datadog.DatadogNavigationObserver(datadogSdk: _datadogSDK); + + @override + NavigatorObserver get navigatorObserver => _navigationObserver; + + /// Sets the [rudderClient] instance in the case of testing. + @visibleForTesting + // ignore: use_setters_to_change_properties + void setDatadogSdk(datadog.DatadogSdk rudderClient) { + _datadogSDK = rudderClient; + } + + @override + Future setup(DerivDatadogConfiguration configuration) async { + try { + final datadog.RumConfiguration rumConfiguration = + datadog.RumConfiguration( + applicationId: configuration.applicationId, + sessionSamplingRate: configuration.sessionSamplingRate ?? 100, + tracingSamplingRate: configuration.tracingSamplingRate ?? 100, + ); + + final datadog.DdSdkConfiguration datadogConfiguration = + datadog.DdSdkConfiguration( + clientToken: configuration.clientToken, + env: configuration.env, + serviceName: configuration.serviceName, + site: configuration.site?.site ?? DatadogSite.us1.site, + trackingConsent: configuration.trackingConsent.consent, + nativeCrashReportEnabled: + configuration.nativeCrashReportEnabled ?? true, + loggingConfiguration: datadog.LoggingConfiguration(), + rumConfiguration: rumConfiguration, + ); + + final FlutterExceptionHandler? originalOnError = FlutterError.onError; + FlutterError.onError = (FlutterErrorDetails details) { + _datadogSDK.rum?.handleFlutterError(details); + originalOnError?.call(details); + }; + final ErrorCallback? platformOriginalOnError = + PlatformDispatcher.instance.onError; + PlatformDispatcher.instance.onError = (Object e, StackTrace st) { + _datadogSDK.rum?.addErrorInfo( + e.toString(), + datadog.RumErrorSource.source, + stackTrace: st, + ); + return platformOriginalOnError?.call(e, st) ?? false; + }; + + await _datadogSDK.initialize(datadogConfiguration); + _datadogSDK.updateConfigurationInfo( + LateConfigurationProperty.trackErrors, true); + return true; + } on Exception { + return false; + } + } + + /// Sets the user information for the current session. + void setUserInfo({ + String? id, + String? name, + String? email, + Map extraInfo = const {}, + }) => + _datadogSDK.setUserInfo( + id: id, + name: name, + email: email, + extraInfo: extraInfo, + ); + + /// Logs Custom Event + void logEvent({ + required RumUserActionType type, + required String name, + Map attributes = const {}, + }) => + _datadogSDK.rum?.addUserAction( + type.rumUserActionType, + name, + attributes, + ); +} diff --git a/packages/analytics/lib/sdk/firebase/core/firebase_configuration.dart b/packages/analytics/lib/sdk/firebase/core/firebase_configuration.dart new file mode 100644 index 000000000..7862d7642 --- /dev/null +++ b/packages/analytics/lib/sdk/firebase/core/firebase_configuration.dart @@ -0,0 +1,12 @@ +import 'package:analytics/sdk/base_analytics_configuration.dart'; + +/// A class to define the configuration options for [FirebaseConfiguration]. +class FirebaseConfiguration implements BaseAnalyticsConfiguration { + /// Creates a new [FirebaseConfiguration] instance with the given options. + const FirebaseConfiguration({required this.isAnalyticsCollectionEnabled}); + + /// Sets whether analytics collection is enabled for this app on this device. + /// + /// This setting is persisted across app sessions. + final bool isAnalyticsCollectionEnabled; +} diff --git a/packages/analytics/lib/sdk/firebase/sdk/deriv_firebase_analytics.dart b/packages/analytics/lib/sdk/firebase/sdk/deriv_firebase_analytics.dart new file mode 100644 index 000000000..9b840e31f --- /dev/null +++ b/packages/analytics/lib/sdk/firebase/sdk/deriv_firebase_analytics.dart @@ -0,0 +1,105 @@ +import 'dart:async'; + +import 'package:analytics/analytics_route_observer.dart'; +import 'package:analytics/core/logger.dart'; +import 'package:analytics/sdk/base_analytics.dart'; +import 'package:analytics/sdk/firebase/core/firebase_configuration.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:flutter/widgets.dart'; + +/// A wrapper around Firebase Flutter SDK. +class DerivFirebaseAnalytics implements BaseAnalytics { + /// Creates a new [DerivFirebaseAnalytics] instance. + factory DerivFirebaseAnalytics(FirebaseAnalytics firebaseAnalytics) { + _instance._firebaseAnalytics = firebaseAnalytics; + return _instance; + } + + DerivFirebaseAnalytics._internal(); + + static final DerivFirebaseAnalytics _instance = + DerivFirebaseAnalytics._internal(); + + @override + NavigatorObserver get navigatorObserver => + AnalyticsRouteObserver(onNewRoute: (Route route) { + if (route.settings.name != null) { + setCurrentScreen( + screenName: route.settings.name!, + ); + } + }); + + late FirebaseAnalytics _firebaseAnalytics; + + final Logger _logger = ConsoleLogger(); + + /// Sets up the Firebase client. + /// + /// This method must be called before any other method. + @override + Future setup(FirebaseConfiguration configuration) => _execute(() async { + await _firebaseAnalytics.setAnalyticsCollectionEnabled( + configuration.isAnalyticsCollectionEnabled); + }); + + /// Logs the passed [screenName] to firebase analytics. + /// [screenName] is required and the method returns true if logging completed + /// Successfully, otherwise, a false is returned. + Future setCurrentScreen({required String screenName}) async => + _execute(() async { + await _firebaseAnalytics.setCurrentScreen(screenName: screenName); + }); + + /// Logs the standard login event. + /// + /// Apps with a login feature can report this event to signify that a user has logged in. + Future logLogin({ + String? loginMethod, + }) async => + _execute(() async { + await _firebaseAnalytics.logLogin(loginMethod: loginMethod); + }); + + /// Logs a custom Flutter Analytics event with the given [name] and event [parameters]. + Future logEvent({ + required String name, + Map? parameters, + }) async => + _execute(() async { + await _firebaseAnalytics.logEvent( + name: name, + parameters: parameters, + ); + }); + + /// Sets the user ID property. + /// + /// Setting a null [id] removes the user id. + Future setUserId({ + required String id, + }) async => + _execute(() async { + await _firebaseAnalytics.setUserId(id: id); + }); + + /// Sets a user property to a given value. + Future setUserProperty({ + required String name, + required String value, + }) async => + _execute(() async { + await _firebaseAnalytics.setUserProperty(name: name, value: value); + }); + + /// Executes [action] and logs errors, if any. + Future _execute(Function action) async { + try { + action(); + return true; + } on Exception catch (e) { + _logger.log('DerivFirebase: $e'); + return false; + } + } +} diff --git a/packages/analytics/lib/sdk/rudderstack/core/rudderstack_configuration.dart b/packages/analytics/lib/sdk/rudderstack/core/rudderstack_configuration.dart new file mode 100644 index 000000000..4465aef12 --- /dev/null +++ b/packages/analytics/lib/sdk/rudderstack/core/rudderstack_configuration.dart @@ -0,0 +1,20 @@ +import 'package:analytics/sdk/base_analytics_configuration.dart'; + +/// A class to define the configuration options for [RudderstackConfiguration]. +class RudderstackConfiguration implements BaseAnalyticsConfiguration { + /// Creates a new [RudderstackConfiguration] instance with the given options. + const RudderstackConfiguration({ + required this.dataPlaneUrl, + required this.writeKey, + this.debugEnabled = false, + }); + + /// The data plane url used to identify the app in the `Rudderstack` dashboard. + final String dataPlaneUrl; + + /// The write key used to authenticate with the `Rudderstack API`. + final String writeKey; + + /// Whether to enable debug mode or not. + final bool debugEnabled; +} diff --git a/packages/analytics/lib/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart b/packages/analytics/lib/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart new file mode 100644 index 000000000..06f366130 --- /dev/null +++ b/packages/analytics/lib/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart @@ -0,0 +1,109 @@ +import 'dart:async'; + +import 'package:analytics/analytics_route_observer.dart'; +import 'package:analytics/core/logger.dart'; +import 'package:analytics/sdk/base_analytics.dart'; +import 'package:analytics/sdk/rudderstack/core/rudderstack_configuration.dart'; +import 'package:flutter/widgets.dart'; +import 'package:rudder_sdk_flutter_platform_interface/platform.dart'; +import 'package:rudder_sdk_flutter/RudderController.dart'; + +/// A wrapper around RudderStack Flutter SDK. +class DerivRudderstack implements BaseAnalytics { + /// Creates a new [DerivRudderstack] instance. + factory DerivRudderstack() => _instance; + + DerivRudderstack._internal(); + + static final DerivRudderstack _instance = DerivRudderstack._internal(); + + @override + NavigatorObserver get navigatorObserver => + AnalyticsRouteObserver(onNewRoute: (Route route) { + if (route.settings.name != null) { + screen( + screenName: route.settings.name!, + ); + } + }); + + RudderController _rudderClient = RudderController.instance; + Logger _logger = ConsoleLogger(); + + /// The [RudderController] instance used for tracking events. + RudderController get rudderClient => _rudderClient; + + /// The [Logger] instance used for logging messages and errors. + Logger get logger => _logger; + + /// Sets the [rudderClient] instance in the case of testing. + @visibleForTesting + set rudderClient(RudderController rudderClient) { + _rudderClient = rudderClient; + } + + /// Sets the [logger] instance in the case of testing. + @visibleForTesting + set logger(Logger logger) { + _logger = logger; + } + + /// Sets the user id for this instance. + Future identify({required String userId}) async => + _execute(() => rudderClient.identify(userId)); + + /// Tracks an event with the given [eventName]. + Future track({required String eventName}) async => + _execute(() => rudderClient.track(eventName)); + + /// Logs a screen view with the given [screenName]. + Future screen({required String screenName}) async => + _execute(() => rudderClient.screen(screenName)); + + /// Adds a user to a group with the given [groupId]. + Future group({required String groupId}) async => + _execute(() => rudderClient.group(groupId)); + + /// Aliases a user with the given alias. + Future alias({required String alias}) async => + _execute(() => rudderClient.alias(alias)); + + /// Sets up the RudderStack client. + /// + /// Takes [dataPlaneUrl] and [writeKey] as parameters. + @override + Future setup(RudderstackConfiguration configuration) async => + _execute(() { + final RudderConfigBuilder builder = RudderConfigBuilder() + ..withDataPlaneUrl(configuration.dataPlaneUrl) + ..withDebug(configuration.debugEnabled); + rudderClient.initialize(configuration.writeKey, + config: builder.build()); + }); + + /// Resets the RudderStack client state. + Future reset() async => _execute(() => rudderClient.reset()); + + /// Disables the RudderStack client. + Future disable() async => _execute(() => rudderClient.optOut(true)); + + /// Enables the RudderStack client. + Future enable() async => _execute(() => rudderClient.optOut(false)); + + /// Sets the context for the RudderStack client. + /// + /// Takes a [token] as a parameter. + Future setContext({required String token}) async => + _execute(() => rudderClient.putDeviceToken(token)); + + /// Executes [action] and logs errors, if any. + Future _execute(Function action) async { + try { + action(); + return true; + } on Exception catch (e) { + logger.log('DerivRudderstack: $e'); + return false; + } + } +} diff --git a/packages/analytics/pubspec.yaml b/packages/analytics/pubspec.yaml index 1f61a025b..c49dcbcb8 100644 --- a/packages/analytics/pubspec.yaml +++ b/packages/analytics/pubspec.yaml @@ -1,6 +1,6 @@ name: analytics description: A new Flutter package for collecting and sending analytical information from the app. -version: 1.0.0 +version: 2.0.0 homepage: https://deriv.com/ publish_to: "none" @@ -10,20 +10,18 @@ environment: flutter: ">=3.10.0" dependencies: - deriv_rudderstack: - git: - url: git@github.com:regentmarkets/flutter-deriv-packages.git - path: packages/deriv_rudderstack - ref: dev - - firebase_analytics: ^10.4.0 - firebase_core: ^2.12.0 + firebase_analytics: ^10.6.2 + firebase_core: ^2.20.0 + rudder_sdk_flutter: ^2.6.0 + datadog_flutter_plugin: ^1.6.2 + flutter: sdk: flutter dev_dependencies: flutter_test: sdk: flutter + mocktail: ^1.0.1 flutter: diff --git a/packages/analytics/test/datadog/core/deriv_datadog_configuration_test.dart b/packages/analytics/test/datadog/core/deriv_datadog_configuration_test.dart new file mode 100644 index 000000000..eee33ff41 --- /dev/null +++ b/packages/analytics/test/datadog/core/deriv_datadog_configuration_test.dart @@ -0,0 +1,31 @@ +import 'package:analytics/sdk/datadog/core/datadog_configuration.dart'; +import 'package:analytics/sdk/datadog/core/enums.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('DerivDatadogConfiguration', () { + test('DerivDatadogConfiguration', () { + const DerivDatadogConfiguration config = DerivDatadogConfiguration( + applicationId: 'myAppId', + clientToken: 'myClientToken', + env: 'development', + trackingConsent: TrackingConsent.granted, + site: DatadogSite.ap1, + nativeCrashReportEnabled: false, + sessionSamplingRate: 10, + tracingSamplingRate: 20, + serviceName: 'myServiceName', + ); + + expect(config.applicationId, 'myAppId'); + expect(config.clientToken, 'myClientToken'); + expect(config.env, 'development'); + expect(config.trackingConsent, TrackingConsent.granted); + expect(config.site, DatadogSite.ap1); + expect(config.nativeCrashReportEnabled, false); + expect(config.sessionSamplingRate, 10); + expect(config.tracingSamplingRate, 20); + expect(config.serviceName, 'myServiceName'); + }); + }); +} diff --git a/packages/analytics/test/datadog/core/enums_test.dart b/packages/analytics/test/datadog/core/enums_test.dart new file mode 100644 index 000000000..66a44497a --- /dev/null +++ b/packages/analytics/test/datadog/core/enums_test.dart @@ -0,0 +1,27 @@ +import 'package:analytics/sdk/datadog/core/enums.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart' as datadog; + +void main() { + group('Enums', () { + test( + 'TrackingConsentExtension: consent should return the corresponding datadog.TrackingConsent value', + () { + expect(TrackingConsent.granted.consent, datadog.TrackingConsent.granted); + expect(TrackingConsent.notGranted.consent, + datadog.TrackingConsent.notGranted); + expect(TrackingConsent.pending.consent, datadog.TrackingConsent.pending); + }); + + test( + 'DatadogSiteExtension: site should return the corresponding datadog.DatadogSite value', + () { + expect(DatadogSite.us1.site, datadog.DatadogSite.us1); + expect(DatadogSite.us3.site, datadog.DatadogSite.us3); + expect(DatadogSite.us5.site, datadog.DatadogSite.us5); + expect(DatadogSite.eu1.site, datadog.DatadogSite.eu1); + expect(DatadogSite.us1Fed.site, datadog.DatadogSite.us1Fed); + expect(DatadogSite.ap1.site, datadog.DatadogSite.ap1); + }); + }); +} diff --git a/packages/analytics/test/datadog/sdk/deriv_datadog_test.dart b/packages/analytics/test/datadog/sdk/deriv_datadog_test.dart new file mode 100644 index 000000000..afd8b05f1 --- /dev/null +++ b/packages/analytics/test/datadog/sdk/deriv_datadog_test.dart @@ -0,0 +1,47 @@ +import 'package:analytics/sdk/base_analytics.dart'; +import 'package:analytics/sdk/datadog/core/datadog_configuration.dart'; +import 'package:analytics/sdk/datadog/sdk/deriv_datadog.dart'; +import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart' as datadog; +import 'package:analytics/sdk/datadog/core/enums.dart' as deriv_datadog_enums; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('DerivDatadog', () { + late DerivDatadog derivDatadog; + late DatadogSdk datadogSdk; + late DerivDatadogConfiguration derivDatadogConfiguration; + + setUpAll(() { + DatadogSdk.initializeForTesting(); + datadogSdk = DatadogSdk.instance; + derivDatadog = DerivDatadog()..setDatadogSdk(datadogSdk); + derivDatadogConfiguration = const DerivDatadogConfiguration( + applicationId: 'applicationId', + clientToken: 'clientToken', + env: 'env', + trackingConsent: deriv_datadog_enums.TrackingConsent.granted, + ); + }); + + test('should return correct instance of DerivDatadog', () { + expect(derivDatadog, isA()); + expect(derivDatadog, isA>()); + }); + + test('should return correct NavigatorObserver', () { + final NavigatorObserver navigationObserver = + derivDatadog.navigatorObserver; + expect(navigationObserver, isNotNull); + expect(navigationObserver, isA()); + }); + + test('should call setup with correct parameters', () async { + final bool result = await derivDatadog.setup(derivDatadogConfiguration); + expect(result, true); + }); + }); +} diff --git a/packages/analytics/test/firebase/core/deriv_firebase_analytics_configuration_test.dart b/packages/analytics/test/firebase/core/deriv_firebase_analytics_configuration_test.dart new file mode 100644 index 000000000..1bd5a0097 --- /dev/null +++ b/packages/analytics/test/firebase/core/deriv_firebase_analytics_configuration_test.dart @@ -0,0 +1,20 @@ +import 'package:analytics/sdk/firebase/core/firebase_configuration.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('FirebaseConfiguration', () { + test('FirebaseConfiguration with isAnalyticsCollectionEnabled true', () { + const FirebaseConfiguration config = + FirebaseConfiguration(isAnalyticsCollectionEnabled: true); + + expect(config.isAnalyticsCollectionEnabled, isTrue); + }); + + test('FirebaseConfiguration with isAnalyticsCollectionEnabled false', () { + const FirebaseConfiguration config = + FirebaseConfiguration(isAnalyticsCollectionEnabled: false); + + expect(config.isAnalyticsCollectionEnabled, isFalse); + }); + }); +} diff --git a/packages/analytics/test/firebase/sdk/deriv_firebase_sdk_test.dart b/packages/analytics/test/firebase/sdk/deriv_firebase_sdk_test.dart new file mode 100644 index 000000000..7afa990fb --- /dev/null +++ b/packages/analytics/test/firebase/sdk/deriv_firebase_sdk_test.dart @@ -0,0 +1,107 @@ +import 'package:analytics/sdk/firebase/core/firebase_configuration.dart'; +import 'package:analytics/sdk/firebase/sdk/deriv_firebase_analytics.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +class MockFirebaseAnalytics extends Mock implements FirebaseAnalytics {} + +void main() { + late DerivFirebaseAnalytics derivFirebaseAnalytics; + late MockFirebaseAnalytics mockFirebaseAnalytics; + + setUp(() { + mockFirebaseAnalytics = MockFirebaseAnalytics(); + derivFirebaseAnalytics = DerivFirebaseAnalytics(mockFirebaseAnalytics); + }); + + group('DerivFirebaseAnalytics', () { + test('should return correct instance of DerivFirebaseAnalytics', () async { + when(() => mockFirebaseAnalytics.setAnalyticsCollectionEnabled(true)) + .thenAnswer((_) async => true); + const FirebaseConfiguration firebaseConfiguration = + FirebaseConfiguration(isAnalyticsCollectionEnabled: true); + + await derivFirebaseAnalytics.setup(firebaseConfiguration); + + verify(() => mockFirebaseAnalytics.setAnalyticsCollectionEnabled( + firebaseConfiguration.isAnalyticsCollectionEnabled)).called(1); + }); + + test('should return correct NavigatorObserver', () async { + const String screenName = 'test_screen_name'; + + when(() => mockFirebaseAnalytics.setCurrentScreen(screenName: screenName)) + .thenAnswer((_) async => true); + + await derivFirebaseAnalytics.setCurrentScreen(screenName: screenName); + + verify(() => + mockFirebaseAnalytics.setCurrentScreen(screenName: screenName)) + .called(1); + }); + + test('should call logLogin with correct parameters', () async { + const String loginMethod = 'test_login_method'; + + when(() => mockFirebaseAnalytics.logLogin(loginMethod: loginMethod)) + .thenAnswer((_) async => true); + + await derivFirebaseAnalytics.logLogin(loginMethod: loginMethod); + + verify(() => mockFirebaseAnalytics.logLogin(loginMethod: loginMethod)) + .called(1); + }); + + test('should call setUserId with correct parameters', () async { + const String userId = 'test_user_id'; + + when(() => mockFirebaseAnalytics.setUserId(id: userId)) + .thenAnswer((_) async => true); + + await derivFirebaseAnalytics.setUserId(id: userId); + + verify(() => mockFirebaseAnalytics.setUserId(id: userId)).called(1); + }); + + test('should call logEvent with correct parameters', () async { + const String name = 'test_name'; + const Map params = {}; + + when(() => mockFirebaseAnalytics.logEvent( + name: name, + parameters: params, + )).thenAnswer((_) async => true); + + await derivFirebaseAnalytics.logEvent( + name: name, + parameters: params, + ); + + verify(() => mockFirebaseAnalytics.logEvent( + name: name, + parameters: params, + )).called(1); + }); + + test('should call setUserProperty with correct parameters', () async { + const String name = 'test_name'; + const String value = 'test_value'; + + when(() => mockFirebaseAnalytics.setUserProperty( + name: name, + value: value, + )).thenAnswer((_) async => true); + + await derivFirebaseAnalytics.setUserProperty( + name: name, + value: value, + ); + + verify(() => mockFirebaseAnalytics.setUserProperty( + name: name, + value: value, + )).called(1); + }); + }); +} diff --git a/packages/analytics/test/rudderstack/core/deriv_rudderstack_configuration_test.dart b/packages/analytics/test/rudderstack/core/deriv_rudderstack_configuration_test.dart new file mode 100644 index 000000000..b0f121eb2 --- /dev/null +++ b/packages/analytics/test/rudderstack/core/deriv_rudderstack_configuration_test.dart @@ -0,0 +1,18 @@ +import 'package:analytics/sdk/rudderstack/core/rudderstack_configuration.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('RudderstackConfiguration', () { + test('RudderstackConfiguration', () { + const String dataPlaneUrl = 'https://test.dataplane.rudderstack.com'; + const String writeKey = 'test_write_key'; + const RudderstackConfiguration config = RudderstackConfiguration( + dataPlaneUrl: dataPlaneUrl, + writeKey: writeKey, + ); + + expect(config.dataPlaneUrl, dataPlaneUrl); + expect(config.writeKey, writeKey); + }); + }); +} diff --git a/packages/analytics/test/rudderstack/sdk/deriv_rudderstack_sdk_test.dart b/packages/analytics/test/rudderstack/sdk/deriv_rudderstack_sdk_test.dart new file mode 100644 index 000000000..09804da8c --- /dev/null +++ b/packages/analytics/test/rudderstack/sdk/deriv_rudderstack_sdk_test.dart @@ -0,0 +1,142 @@ +import 'package:analytics/core/logger.dart'; +import 'package:analytics/sdk/rudderstack/core/rudderstack_configuration.dart'; +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:rudder_sdk_flutter/RudderController.dart'; + +class MockRudderController extends Mock implements RudderController {} + +class MockLogger extends Mock implements Logger {} + +void main() { + late DerivRudderstack derivRudderstack; + late MockRudderController mockRudderController; + late MockLogger mockLogger; + + setUp(() { + mockRudderController = MockRudderController(); + mockLogger = MockLogger(); + derivRudderstack = DerivRudderstack() + ..rudderClient = mockRudderController + ..logger = mockLogger; + }); + + group('DerivRudderstack', () { + test('identify calls rudderClient.identify', () async { + const String userId = 'test_user_id'; + + final bool result = await derivRudderstack.identify(userId: userId); + + expect(result, isTrue); + verify(() => mockRudderController.identify(userId)).called(1); + }); + + test('track calls rudderClient.track', () async { + const String eventName = 'test_event_name'; + + final bool result = await derivRudderstack.track(eventName: eventName); + + expect(result, isTrue); + verify(() => mockRudderController.track(eventName)).called(1); + }); + + test('track calls rudderClient.track', () async { + const String eventName = 'test_event_name'; + + final bool result = await derivRudderstack.track(eventName: eventName); + + expect(result, isTrue); + verify(() => mockRudderController.track(eventName)).called(1); + }); + + test('screen calls rudderClient.screen', () async { + const String screenName = 'test_screen_name'; + + final bool result = await derivRudderstack.screen(screenName: screenName); + + expect(result, isTrue); + verify(() => mockRudderController.screen(screenName)).called(1); + }); + + test('group calls rudderClient.group', () async { + const String groupId = 'test_group_id'; + + final bool result = await derivRudderstack.group(groupId: groupId); + + expect(result, isTrue); + verify(() => mockRudderController.group(groupId)).called(1); + }); + + test('alias calls rudderClient.alias', () async { + const String alias = 'test_alias'; + + final bool result = await derivRudderstack.alias(alias: alias); + + expect(result, isTrue); + verify(() => mockRudderController.alias(alias)).called(1); + }); + + test('should call initialize with correct parameters', () async { + const String dataPlaneUrl = 'https://test.dataplane.rudderstack.com'; + const String writeKey = 'test_write_key'; + + await derivRudderstack.setup(const RudderstackConfiguration( + dataPlaneUrl: dataPlaneUrl, writeKey: writeKey)); + verify(() => mockRudderController.initialize(writeKey, + config: any(named: 'config'))).called(1); + + final bool result = await derivRudderstack.setup( + const RudderstackConfiguration( + dataPlaneUrl: dataPlaneUrl, writeKey: writeKey)); + + expect(result, true); + }); + + test('should throw exception when initialize is called with wrong url', + () async { + const String dataPlaneUrl = 'wrong_url'; + const String writeKey = 'test_write_key'; + + when(() => mockRudderController.initialize(any(), config: any(named: 'config'))) + .thenThrow(Exception()); + + final bool result = await derivRudderstack.setup( + const RudderstackConfiguration( + dataPlaneUrl: dataPlaneUrl, writeKey: writeKey)); + + expect(result, false); + + }); + + test('reset calls rudderClient.reset', () async { + final bool result = await derivRudderstack.reset(); + + expect(result, isTrue); + verify(() => mockRudderController.reset()).called(1); + }); + + test('disable calls rudderClient.optOut(true)', () async { + final bool result = await derivRudderstack.disable(); + + expect(result, isTrue); + verify(() => mockRudderController.optOut(true)).called(1); + }); + + test('enable calls rudderClient.optOut(false)', () async { + final bool result = await derivRudderstack.enable(); + + expect(result, isTrue); + verify(() => mockRudderController.optOut(false)).called(1); + }); + + test('setContext calls rudderClient.putDeviceToken', () async { + const String token = 'test_token'; + + final bool result = await derivRudderstack.setContext(token: token); + + expect(result, isTrue); + verify(() => mockRudderController.putDeviceToken(token)).called(1); + }); + }); +} From 81a3a0df27208bd200009415855c6cb944d016e3 Mon Sep 17 00:00:00 2001 From: Bassam El Obeid Date: Fri, 8 Dec 2023 15:24:55 +0400 Subject: [PATCH 59/63] revert(analytics): versioning and CHANGELOG - Introduced a flexible configuration system to support multiple analytics providers. - Added `BaseAnalyticsConfiguration` interface for standardizing analytics configuration. - Internalized and restructured Datadog analytics by providing a `DerivDatadog` class that implements `BaseAnalytics`. - Refined existing analytics services to have their own classes implementing `BaseAnalytics`. - Introduced Enum types and extensions (`TrackingConsent`, `DatadogSite`) to aid in analytics configuration. - Improved navigation observer support to work with multiple analytics providers. - Enhanced exception handling to capture Flutter and Platform-specific errors. - Added ability to set user information for analytics tracking. - Replaced in-house Rudderstack plugin with the official Rudderstack SDK. ### Breaking changes: **General**: - `setup()` method for analytics providers now requires configuration classes implementing `BaseAnalyticsConfiguration`. - Exception handling updated to include multiple analytics providers. - Rudderstack: Now requires the official Rudderstack SDK for analytics. Update dependencies accordingly. - Datadog: Migrated from a separate package to an internal implementation within this package. - Existing analytics services have been restructured to implement `BaseAnalytics`, potentially changing function signatures and configurations. --- packages/analytics/CHANGELOG.md | 19 ------------------- packages/analytics/pubspec.yaml | 2 +- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/packages/analytics/CHANGELOG.md b/packages/analytics/CHANGELOG.md index 896b85d94..0a228ceff 100644 --- a/packages/analytics/CHANGELOG.md +++ b/packages/analytics/CHANGELOG.md @@ -1,22 +1,3 @@ -## [2.0.0] -- Introduced a flexible configuration system to support multiple analytics providers. -- Added `BaseAnalyticsConfiguration` interface for standardizing analytics configuration. -- Internalized and restructured Datadog analytics by providing a `DerivDatadog` class that implements `BaseAnalytics`. -- Refined existing analytics services to have their own classes implementing `BaseAnalytics`. -- Introduced Enum types and extensions (`TrackingConsent`, `DatadogSite`) to aid in analytics configuration. -- Improved navigation observer support to work with multiple analytics providers. -- Enhanced exception handling to capture Flutter and Platform-specific errors. -- Added ability to set user information for analytics tracking. -- Replaced in-house Rudderstack plugin with the official Rudderstack SDK. - -### Breaking changes: -**General**: -- `setup()` method for analytics providers now requires configuration classes implementing `BaseAnalyticsConfiguration`. -- Exception handling updated to include multiple analytics providers. -- Rudderstack: Now requires the official Rudderstack SDK for analytics. Update dependencies accordingly. -- Datadog: Migrated from a separate package to an internal implementation within this package. -- Existing analytics services have been restructured to implement `BaseAnalytics`, potentially changing function signatures and configurations. - ## [1.0.0] - Migrated the package to null safety. diff --git a/packages/analytics/pubspec.yaml b/packages/analytics/pubspec.yaml index c49dcbcb8..d8aa8a46d 100644 --- a/packages/analytics/pubspec.yaml +++ b/packages/analytics/pubspec.yaml @@ -1,6 +1,6 @@ name: analytics description: A new Flutter package for collecting and sending analytical information from the app. -version: 2.0.0 +version: 1.0.0 homepage: https://deriv.com/ publish_to: "none" From 16ad4a811e942c037f7370ab326c8e310d469835 Mon Sep 17 00:00:00 2001 From: mobile-apps-deriv <134251399+mobile-apps-deriv@users.noreply.github.com> Date: Fri, 8 Dec 2023 19:32:11 +0800 Subject: [PATCH 60/63] chore(release): publish packages (#356) - analytics@1.0.1 Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 22 ++++++++++++++++++++++ packages/analytics/CHANGELOG.md | 5 +++++ packages/analytics/pubspec.yaml | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c95b0856..5999f79a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2023-12-08 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`analytics` - `v1.0.1`](#analytics---v101) + +--- + +#### `analytics` - `v1.0.1` + + - **REVERT**(analytics): versioning and CHANGELOG. ([81a3a0df](https://github.com/regentmarkets/flutter-deriv-packages/commit/81a3a0df27208bd200009415855c6cb944d016e3)) + - **REFACTOR**(analytics): [MOBC-546] Creating unified analytics package. ([#315](https://github.com/regentmarkets/flutter-deriv-packages/issues/315)). ([fd1d8ed3](https://github.com/regentmarkets/flutter-deriv-packages/commit/fd1d8ed345d4ecf91c5f6c1463c5196b40abcbf6)) + + ## 2023-12-04 ### Changes diff --git a/packages/analytics/CHANGELOG.md b/packages/analytics/CHANGELOG.md index 0a228ceff..df6cadb3a 100644 --- a/packages/analytics/CHANGELOG.md +++ b/packages/analytics/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.1 + + - **REVERT**(analytics): versioning and CHANGELOG. ([81a3a0df](https://github.com/regentmarkets/flutter-deriv-packages/commit/81a3a0df27208bd200009415855c6cb944d016e3)) + - **REFACTOR**(analytics): [MOBC-546] Creating unified analytics package. ([#315](https://github.com/regentmarkets/flutter-deriv-packages/issues/315)). ([fd1d8ed3](https://github.com/regentmarkets/flutter-deriv-packages/commit/fd1d8ed345d4ecf91c5f6c1463c5196b40abcbf6)) + ## [1.0.0] - Migrated the package to null safety. diff --git a/packages/analytics/pubspec.yaml b/packages/analytics/pubspec.yaml index d8aa8a46d..2aa153901 100644 --- a/packages/analytics/pubspec.yaml +++ b/packages/analytics/pubspec.yaml @@ -1,6 +1,6 @@ name: analytics description: A new Flutter package for collecting and sending analytical information from the app. -version: 1.0.0 +version: 1.0.1 homepage: https://deriv.com/ publish_to: "none" From f0088c6f36e7ead06ced72db60c04b4d360cf298 Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Tue, 12 Dec 2023 17:42:27 +0800 Subject: [PATCH 61/63] ci: replace tag with commit hash on third-party actions (#350) --- .github/workflows/all_packages.yaml | 8 ++++---- .github/workflows/pr_title.yaml | 2 +- .github/workflows/version.yaml | 17 +++++------------ 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/.github/workflows/all_packages.yaml b/.github/workflows/all_packages.yaml index 6e25797c2..fb3c00d1d 100644 --- a/.github/workflows/all_packages.yaml +++ b/.github/workflows/all_packages.yaml @@ -18,22 +18,22 @@ jobs: runs-on: ubuntu-latest steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - name: Setup Flutter - uses: subosito/flutter-action@v2 + uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa with: channel: "stable" flutter-version: "3.10.2" cache: true - name: Set SSH Key - uses: webfactory/ssh-agent@v0.8.0 + uses: webfactory/ssh-agent@fd34b8dee206fe74b288a5e61bc95fba2f1911eb with: ssh-private-key: ${{secrets.SSH_PRIVATE_KEY}} - name: Install Melos and run pub get - uses: bluefireteam/melos-action@v1 + uses: bluefireteam/melos-action@dd3c344d731938d2ab2567a261f54a19a68b5f6a with: melos-version: "3.0.1" diff --git a/.github/workflows/pr_title.yaml b/.github/workflows/pr_title.yaml index 731c27830..b40f4d73d 100644 --- a/.github/workflows/pr_title.yaml +++ b/.github/workflows/pr_title.yaml @@ -14,6 +14,6 @@ jobs: validate: runs-on: ubuntu-latest steps: - - uses: amannn/action-semantic-pull-request@v5 + - uses: amannn/action-semantic-pull-request@e9fabac35e210fea40ca5b14c0da95a099eff26f env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/version.yaml b/.github/workflows/version.yaml index c93910aab..ab36288f7 100644 --- a/.github/workflows/version.yaml +++ b/.github/workflows/version.yaml @@ -7,48 +7,41 @@ on: 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 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 with: ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} fetch-depth: 0 - name: Setup Git User - uses: fregante/setup-git-user@v2 + uses: fregante/setup-git-user@77c1b5542f14ab6db4b8462d6857e31deb988b09 - name: Setup Flutter - uses: subosito/flutter-action@v2 + uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa with: channel: "stable" flutter-version: "3.10.2" cache: true - name: Setup Melos - uses: bluefireteam/melos-action@v3 + uses: bluefireteam/melos-action@dd3c344d731938d2ab2567a261f54a19a68b5f6a 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 --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 + uses: peter-evans/create-pull-request@76c6f5c20e2111bfee3cd30fae52a25e410f5efc with: token: ${{ secrets.PAT }} title: "chore(version): bump version and update changelog" From 92426f15eaa5caa529724590e006fd0f65d6800e Mon Sep 17 00:00:00 2001 From: sahani-deriv <125638269+sahani-deriv@users.noreply.github.com> Date: Tue, 12 Dec 2023 17:53:38 +0800 Subject: [PATCH 62/63] refactor(deriv_dependency_injector): [MOBC-534] deprecate deriv dependency injector (#347) --- packages/deriv_dependency_injector/lib/src/injector.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/deriv_dependency_injector/lib/src/injector.dart b/packages/deriv_dependency_injector/lib/src/injector.dart index c7b5bb832..fbc98ff08 100644 --- a/packages/deriv_dependency_injector/lib/src/injector.dart +++ b/packages/deriv_dependency_injector/lib/src/injector.dart @@ -2,6 +2,10 @@ import 'package:deriv_dependency_injector/dependency_injector.dart'; const String _defaultKey = 'default'; +@Deprecated('This package is deprecated and will be removed in the future. ' + 'Please use the deriv dependency injector package from: ' + 'https://github.com/deriv-com/deriv-dependency-injector') + /// Injector class for dependency injection. class Injector { /// Get the instance of the named injector or create a new one if it doesn't exist. From d4fc040f5798e8dd4fe074111648156596ab5bb0 Mon Sep 17 00:00:00 2001 From: mobile-apps-deriv <134251399+mobile-apps-deriv@users.noreply.github.com> Date: Tue, 12 Dec 2023 18:27:27 +0800 Subject: [PATCH 63/63] chore(release): publish packages (#362) - deriv_dependency_injector@1.0.1 Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 21 +++++++++++++++++++ .../deriv_dependency_injector/CHANGELOG.md | 4 ++++ .../deriv_dependency_injector/pubspec.yaml | 2 +- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5999f79a0..e9b5b6acc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,27 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2023-12-12 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_dependency_injector` - `v1.0.1`](#deriv_dependency_injector---v101) + +--- + +#### `deriv_dependency_injector` - `v1.0.1` + + - **REFACTOR**(deriv_dependency_injector): [MOBC-534] deprecate deriv dependency injector ([#347](https://github.com/regentmarkets/flutter-deriv-packages/issues/347)). ([92426f15](https://github.com/regentmarkets/flutter-deriv-packages/commit/92426f15eaa5caa529724590e006fd0f65d6800e)) + + ## 2023-12-08 ### Changes diff --git a/packages/deriv_dependency_injector/CHANGELOG.md b/packages/deriv_dependency_injector/CHANGELOG.md index effe43c82..87ea0302b 100644 --- a/packages/deriv_dependency_injector/CHANGELOG.md +++ b/packages/deriv_dependency_injector/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + + - **REFACTOR**(deriv_dependency_injector): [MOBC-534] deprecate deriv dependency injector ([#347](https://github.com/regentmarkets/flutter-deriv-packages/issues/347)). ([92426f15](https://github.com/regentmarkets/flutter-deriv-packages/commit/92426f15eaa5caa529724590e006fd0f65d6800e)) + ## 1.0.0 - Initial version. diff --git a/packages/deriv_dependency_injector/pubspec.yaml b/packages/deriv_dependency_injector/pubspec.yaml index 0101e4b74..b32f63df9 100644 --- a/packages/deriv_dependency_injector/pubspec.yaml +++ b/packages/deriv_dependency_injector/pubspec.yaml @@ -1,7 +1,7 @@ name: deriv_dependency_injector description: A package for handling dependency injection in Dart. -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=3.0.0 <4.0.0"