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: