diff --git a/lib/src/chat/chat_create_group_settings.dart b/lib/src/chat/chat_create_group_settings.dart index c6069f2a..9fc1c53e 100644 --- a/lib/src/chat/chat_create_group_settings.dart +++ b/lib/src/chat/chat_create_group_settings.dart @@ -40,8 +40,6 @@ * for more details. */ -import 'dart:io'; - import 'package:delta_chat_core/delta_chat_core.dart' as Core; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/src/chat/chat_profile.dart b/lib/src/chat/chat_profile.dart index 9a050e68..397f3dd0 100644 --- a/lib/src/chat/chat_profile.dart +++ b/lib/src/chat/chat_profile.dart @@ -119,7 +119,7 @@ class _ChatProfileState extends State { )); } - BlocBuilder _buildChatProfileOneToOne(ChatStateSuccess state) { + Widget _buildChatProfileOneToOne(ChatStateSuccess state) { bool _isSelfTalk = state.isSelfTalk; return BlocBuilder( bloc: _contactListBloc, diff --git a/lib/src/log/log_bloc_delegate.dart b/lib/src/log/log_bloc_delegate.dart index f9ae8055..c69c143c 100644 --- a/lib/src/log/log_bloc_delegate.dart +++ b/lib/src/log/log_bloc_delegate.dart @@ -47,8 +47,8 @@ class LogBlocDelegate implements BlocDelegate { final Logger _logger = Logger("blocDelegate"); @override - void onTransition(Bloc bloc, Transition transition) { - _logger.info(transition.toString()); + void onEvent(Bloc bloc, Object event) { + _logger.info(event.toString()); } @override @@ -57,7 +57,7 @@ class LogBlocDelegate implements BlocDelegate { } @override - void onEvent(Bloc bloc, Object event) { - // TODO: implement onEvent + void onTransition(Bloc bloc, Transition transition) { + _logger.info(transition.toString()); } } diff --git a/lib/src/login/login_events_state.dart b/lib/src/login/login_events_state.dart index 3fbd88b4..eb5c8d39 100644 --- a/lib/src/login/login_events_state.dart +++ b/lib/src/login/login_events_state.dart @@ -118,7 +118,9 @@ abstract class LoginState extends ProgressState { LoginState({progress}) : super(progress: progress); } -class LoginStateInitial extends LoginState {} +class LoginStateInitial extends LoginState { + final progress = 0; +} class LoginStateLoading extends LoginState { LoginStateLoading({@required progress}) : super(progress: progress); diff --git a/lib/src/login/login_manual_settings.dart b/lib/src/login/login_manual_settings.dart index fb412f4a..2072ac6b 100644 --- a/lib/src/login/login_manual_settings.dart +++ b/lib/src/login/login_manual_settings.dart @@ -41,13 +41,16 @@ */ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:ox_coi/src/l10n/localizations.dart'; import 'package:ox_coi/src/navigation/navigatable.dart'; import 'package:ox_coi/src/navigation/navigation.dart'; -import 'package:ox_coi/src/settings/settings_manual_mixin.dart'; +import 'package:ox_coi/src/platform/system.dart'; +import 'package:ox_coi/src/settings/settings_manual_form.dart'; +import 'package:ox_coi/src/settings/settings_manual_form_bloc.dart'; +import 'package:ox_coi/src/settings/settings_manual_form_event_state.dart'; import 'package:ox_coi/src/ui/color.dart'; import 'package:ox_coi/src/ui/dimensions.dart'; -import 'package:ox_coi/src/utils/core.dart'; import 'package:ox_coi/src/utils/dialog_builder.dart'; import 'package:ox_coi/src/widgets/fullscreen_progress.dart'; import 'package:rxdart/rxdart.dart'; @@ -61,21 +64,20 @@ class LoginManualSettings extends StatefulWidget { final String email; final String password; - LoginManualSettings({this.success, this.email, this.password, this.fromError}); + LoginManualSettings({@required this.success, @required this.fromError, this.email, this.password}); @override _LoginManualSettingsState createState() => _LoginManualSettingsState(); } -class _LoginManualSettingsState extends State with ManualSettings { +class _LoginManualSettingsState extends State { OverlayEntry _progressOverlayEntry; + FullscreenProgress _progress; LoginBloc _loginBloc = LoginBloc(); @override void initState() { super.initState(); - enabledEmailField.controller.text = widget.email; - passwordField.controller.text = widget.password; var navigation = Navigation(); navigation.current = Navigatable(Type.loginManualSettings); final loginObservable = new Observable(_loginBloc.state); @@ -105,72 +107,123 @@ class _LoginManualSettingsState extends State with ManualSe @override Widget build(BuildContext context) { -// return Scaffold(body: getFormFields(context: context, isLogin: true, fromError: widget.fromError, signIn: _signIn)); - return Scaffold( - body: Column(children: [ - Padding( - padding: EdgeInsets.only( - top: loginManualSettingsPadding, - right: loginManualSettingsPadding, - left: loginManualSettingsPadding, - ), - child: Align( - alignment: Alignment.centerRight, - child: FlatButton( - onPressed: _signIn, - child: Text( - AppLocalizations.of(context).loginSignInButtonText, - style: Theme.of(context).textTheme.subhead.apply(color: accent), - ), + return BlocProvider( + builder: (context) { + var settingsManualFormBloc = SettingsManualFormBloc(); + settingsManualFormBloc.dispatch(SetupSettings( + shouldLoadConfig: false, + email: widget.email, + password: widget.password, + )); + return settingsManualFormBloc; + }, + child: BlocListener( + listener: (BuildContext context, state) { + if (state is SettingsManualFormStateValidationSuccess) { + _progress = FullscreenProgress( + bloc: _loginBloc, + text: AppLocalizations.of(context).loginProgressMessage, + showProgressValues: true, + showCancelButton: false, + ); + _progressOverlayEntry = OverlayEntry(builder: (context) => _progress); + OverlayState overlayState = Overlay.of(context); + overlayState.insert(_progressOverlayEntry); + _loginBloc.dispatch(LoginButtonPressed( + email: state.email, + password: state.password, + imapLogin: state.imapLogin, + imapServer: state.imapServer, + imapPort: state.imapPort, + imapSecurity: state.imapSecurity, + smtpLogin: state.smtpLogin, + smtpPassword: state.password, + smtpServer: state.smtpServer, + smtpPort: state.smtpPort, + smtpSecurity: state.smtpSecurity, + )); + } + }, + child: Scaffold( + body: Column( + children: [ + Padding( + padding: EdgeInsets.only( + top: loginManualSettingsPadding, + right: loginManualSettingsPadding, + left: loginManualSettingsPadding, + ), + child: LoginButton(), + ), + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(loginManualSettingsPadding), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + AppLocalizations.of(context).loginManualSettings, + style: Theme.of(context).textTheme.headline, + ), + Padding( + padding: EdgeInsets.only(top: loginVerticalPadding8dp), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Visibility( + visible: widget.fromError, + child: Text( + AppLocalizations.of(context).loginManualSettingsErrorInfoText, + ), + ), + Padding(padding: EdgeInsets.all(loginManualSettingsSubTitlePadding)), + Text( + AppLocalizations.of(context).loginManualSettingsInfoText, + textAlign: TextAlign.center, + ), + Padding(padding: EdgeInsets.all(loginManualSettingsSubTitlePadding)), + Text( + AppLocalizations.of(context).loginManualSettingsSecondInfoText, + textAlign: TextAlign.center, + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.only(top: loginVerticalPadding), + child: SettingsManualForm(isLogin: true), + ), + ], + ), + ), + ), + ) + ], ), ), ), - Expanded( - child: getFormFields(context: context, isLogin: true, fromError: widget.fromError), - ) - ])); + ); } +} - void _signIn() { - FocusScope.of(context).requestFocus(FocusNode()); - - var email = enabledEmailField.controller.text; - var password = passwordField.controller.text; - var imapLogin = imapLoginNameField.controller.text; - var imapServer = imapServerField.controller.text; - var imapPort = imapPortField.controller.text; - var imapSecurity = convertProtocolStringToInt(context, selectedImapSecurity); - var smtpLogin = smtpLoginNameField.controller.text; - var smtpPassword = smtpPasswordField.controller.text; - var smtpServer = smtpServerField.controller.text; - var smtpPort = smtpPortField.controller.text; - var smtpSecurity = convertProtocolStringToInt(context, selectedSmtpSecurity); - - bool loginIsValid = formKey.currentState.validate(); - - if (loginIsValid) { - _progressOverlayEntry = OverlayEntry( - builder: (context) => FullscreenProgress( - bloc: _loginBloc, - text: AppLocalizations.of(context).loginProgressMessage, - showProgressValues: true, - showCancelButton: false, +class LoginButton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.centerRight, + child: FlatButton( + onPressed: () => _performLogin(context), + child: Text( + AppLocalizations.of(context).loginSignInButtonText, + style: Theme.of(context).textTheme.subhead.apply(color: accent), ), - ); - Overlay.of(context).insert(_progressOverlayEntry); - _loginBloc.dispatch(LoginButtonPressed( - email: email, - password: password, - imapLogin: imapLogin, - imapServer: imapServer, - imapPort: imapPort, - imapSecurity: imapSecurity, - smtpLogin: smtpLogin, - smtpPassword: smtpPassword, - smtpServer: smtpServer, - smtpPort: smtpPort, - smtpSecurity: smtpSecurity, - )); - } + ), + ); + } + + void _performLogin(BuildContext context) { + unFocus(context); + BlocProvider.of(context).dispatch(RequestValidateSettings()); } } diff --git a/lib/src/login/login_provider_signin.dart b/lib/src/login/login_provider_signin.dart index db0f225b..e198a53c 100644 --- a/lib/src/login/login_provider_signin.dart +++ b/lib/src/login/login_provider_signin.dart @@ -100,6 +100,9 @@ class _ProviderSignInState extends State { } void handleLoginStateChange(LoginState state) { + if (!_navigation.current.equal( Navigatable(Type.loginProviderSignIn))) { + return; + } if (state is LoginStateSuccess || state is LoginStateFailure) { if (_progressOverlayEntry != null) { _progressOverlayEntry.remove(); diff --git a/lib/src/platform/system.dart b/lib/src/platform/system.dart index 9e0919cd..54c67e01 100644 --- a/lib/src/platform/system.dart +++ b/lib/src/platform/system.dart @@ -41,5 +41,8 @@ */ import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; -hideKeyboard() => SystemChannels.textInput.invokeMethod('TextInput.hide'); \ No newline at end of file +hideKeyboard() => SystemChannels.textInput.invokeMethod('TextInput.hide'); + +unFocus(BuildContext context) => FocusScope.of(context).requestFocus(FocusNode()); diff --git a/lib/src/settings/settings_manual_form.dart b/lib/src/settings/settings_manual_form.dart new file mode 100644 index 00000000..2ec1dbff --- /dev/null +++ b/lib/src/settings/settings_manual_form.dart @@ -0,0 +1,229 @@ +/* + * OPEN-XCHANGE legal information + * + * All intellectual property rights in the Software are protected by + * international copyright laws. + * + * + * In some countries OX, OX Open-Xchange and open xchange + * as well as the corresponding Logos OX Open-Xchange and OX are registered + * trademarks of the OX Software GmbH group of companies. + * The use of the Logos is not covered by the Mozilla Public License 2.0 (MPL 2.0). + * Instead, you are allowed to use these Logos according to the terms and + * conditions of the Creative Commons License, Version 2.5, Attribution, + * Non-commercial, ShareAlike, and the interpretation of the term + * Non-commercial applicable to the aforementioned license is published + * on the web site https://www.open-xchange.com/terms-and-conditions/. + * + * Please make sure that third-party modules and libraries are used + * according to their respective licenses. + * + * Any modifications to this package must retain all copyright notices + * of the original copyright holder(s) for the original code used. + * + * After any such modifications, the original and derivative code shall remain + * under the copyright of the copyright holder(s) and/or original author(s) as stated here: + * https://www.open-xchange.com/legal/. The contributing author shall be + * given Attribution for the derivative code and a license granting use. + * + * Copyright (C) 2016-2020 OX Software GmbH + * Mail: info@open-xchange.com + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public License 2.0 + * for more details. + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:ox_coi/src/l10n/localizations.dart'; +import 'package:ox_coi/src/settings/settings_manual_form_bloc.dart'; +import 'package:ox_coi/src/settings/settings_manual_form_event_state.dart'; +import 'package:ox_coi/src/ui/dimensions.dart'; +import 'package:ox_coi/src/ui/text_styles.dart'; +import 'package:ox_coi/src/utils/core.dart'; +import 'package:ox_coi/src/widgets/validatable_text_form_field.dart'; + +class SettingsManualForm extends StatefulWidget { + final bool isLogin; + + const SettingsManualForm({Key key, @required this.isLogin}) : super(key: key); + + @override + _SettingsManualFormState createState() => _SettingsManualFormState(); +} + +class _SettingsManualFormState extends State { + ValidatableTextFormField emailField; + ValidatableTextFormField passwordField = ValidatableTextFormField( + (context) => AppLocalizations.of(context).password, + textFormType: TextFormType.password, + needValidation: true, + validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidPassword, + ); + ValidatableTextFormField imapLoginNameField = ValidatableTextFormField((context) => AppLocalizations.of(context).loginLabelImapName); + ValidatableTextFormField imapServerField = ValidatableTextFormField( + (context) => AppLocalizations.of(context).loginLabelImapServer, + inputType: TextInputType.url, + ); + ValidatableTextFormField imapPortField = ValidatableTextFormField( + (context) => AppLocalizations.of(context).loginLabelImapPort, + textFormType: TextFormType.port, + inputType: TextInputType.number, + needValidation: true, + validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidPort, + ); + ValidatableTextFormField smtpLoginNameField = ValidatableTextFormField((context) => AppLocalizations.of(context).loginLabelSmtpName); + ValidatableTextFormField smtpPasswordField = ValidatableTextFormField( + (context) => AppLocalizations.of(context).loginLabelSmtpPassword, + textFormType: TextFormType.password, + needValidation: true, + validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidPassword, + ); + ValidatableTextFormField smtpServerField = ValidatableTextFormField( + (context) => AppLocalizations.of(context).loginLabelSmtpServer, + inputType: TextInputType.url, + ); + ValidatableTextFormField smtpPortField = ValidatableTextFormField( + (context) => AppLocalizations.of(context).loginLabelSmtpPort, + textFormType: TextFormType.port, + inputType: TextInputType.number, + needValidation: true, + validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidPort, + ); + + @override + void initState() { + super.initState(); + emailField = ValidatableTextFormField( + (context) => AppLocalizations.of(context).emailAddress, + textFormType: TextFormType.email, + inputType: TextInputType.emailAddress, + needValidation: true, + enabled: widget.isLogin, + validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidEmail, + ); + } + + final formKey = GlobalKey(); + String selectedImapSecurity; + String selectedSmtpSecurity; + + @override + Widget build(BuildContext context) { + return BlocListener( + listener: (context, state) { + if (state is SettingsManualFormStateReady) { + emailField.controller.text = state.email; + passwordField.controller.text = state.password; + imapLoginNameField.controller.text = state.imapLogin; + imapServerField.controller.text = state.imapServer; + imapPortField.controller.text = state.imapPort; + smtpLoginNameField.controller.text = state.smtpLogin; + smtpServerField.controller.text = state.imapServer; + smtpPortField.controller.text = state.smtpPort; + selectedImapSecurity = convertProtocolIntToString(context, state.imapSecurity); + selectedSmtpSecurity = convertProtocolIntToString(context, state.smtpSecurity); + } else if (state is SettingsManualFormStateValidation) { + var success = formKey.currentState.validate(); + if (success) { + BlocProvider.of(context).dispatch(ValidationDone( + success: success, + email: emailField.controller.text, + password: passwordField.controller.text, + imapServer: imapServerField.controller.text, + imapPort: imapPortField.controller.text, + imapSecurity: convertProtocolStringToInt(context, selectedImapSecurity), + smtpServer: smtpServerField.controller.text, + smtpPort: smtpPortField.controller.text, + smtpSecurity: convertProtocolStringToInt(context, selectedSmtpSecurity), + )); + } + } + }, + child: Form( + key: formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Align( + alignment: Alignment.centerLeft, + child: Text( + AppLocalizations.of(context).loginBaseSettingsTitle, + style: Theme.of(context).textTheme.subhead.merge(primaryW500), + ), + ), + emailField, + passwordField, + Padding(padding: EdgeInsets.all(loginVerticalPadding12dp)), + Align( + alignment: Alignment.centerLeft, + child: Text( + AppLocalizations.of(context).loginServerAddressesTitle, + style: Theme.of(context).textTheme.subhead.merge(primaryW500), + ), + ), + imapServerField, + smtpServerField, + Padding(padding: EdgeInsets.all(loginVerticalPadding12dp)), + Align( + alignment: Alignment.centerLeft, + child: Text( + AppLocalizations.of(context).loginAdvancedImapTitle, + style: Theme.of(context).textTheme.subhead.merge(primaryW500), + ), + ), + imapPortField, + Padding(padding: EdgeInsets.all(loginVerticalPadding12dp)), + Text(AppLocalizations.of(context).loginLabelImapSecurity), + DropdownButton( + value: selectedImapSecurity == null ? AppLocalizations.of(context).automatic : selectedImapSecurity, + items: getSecurityOptions(context), + onChanged: (String newValue) { + setState(() { + selectedImapSecurity = newValue; + }); + }), + Padding(padding: EdgeInsets.all(loginVerticalPadding12dp)), + Align( + alignment: Alignment.centerLeft, + child: Text( + AppLocalizations.of(context).loginAdvancedSmtpTitle, + style: Theme.of(context).textTheme.subhead.merge(primaryW500), + ), + ), + smtpPortField, + Padding(padding: EdgeInsets.all(loginVerticalPadding12dp)), + Text(AppLocalizations.of(context).loginLabelSmtpSecurity), + DropdownButton( + value: selectedSmtpSecurity == null ? AppLocalizations.of(context).automatic : selectedSmtpSecurity, + items: getSecurityOptions(context), + onChanged: (String newValue) { + setState(() { + selectedSmtpSecurity = newValue; + }); + }, + ), + ], + ), + ), + ); + } + + List> getSecurityOptions(BuildContext context) { + return [ + AppLocalizations.of(context).automatic, + AppLocalizations.of(context).sslTls, + AppLocalizations.of(context).startTLS, + AppLocalizations.of(context).off, + ].map>((String value) { + return DropdownMenuItem(value: value, child: Text(value)); + }).toList(); + } +} diff --git a/lib/src/settings/settings_manual_form_bloc.dart b/lib/src/settings/settings_manual_form_bloc.dart new file mode 100644 index 00000000..34c135ae --- /dev/null +++ b/lib/src/settings/settings_manual_form_bloc.dart @@ -0,0 +1,117 @@ +/* + * OPEN-XCHANGE legal information + * + * All intellectual property rights in the Software are protected by + * international copyright laws. + * + * + * In some countries OX, OX Open-Xchange and open xchange + * as well as the corresponding Logos OX Open-Xchange and OX are registered + * trademarks of the OX Software GmbH group of companies. + * The use of the Logos is not covered by the Mozilla Public License 2.0 (MPL 2.0). + * Instead, you are allowed to use these Logos according to the terms and + * conditions of the Creative Commons License, Version 2.5, Attribution, + * Non-commercial, ShareAlike, and the interpretation of the term + * Non-commercial applicable to the aforementioned license is published + * on the web site https://www.open-xchange.com/terms-and-conditions/. + * + * Please make sure that third-party modules and libraries are used + * according to their respective licenses. + * + * Any modifications to this package must retain all copyright notices + * of the original copyright holder(s) for the original code used. + * + * After any such modifications, the original and derivative code shall remain + * under the copyright of the copyright holder(s) and/or original author(s) as stated here: + * https://www.open-xchange.com/legal/. The contributing author shall be + * given Attribution for the derivative code and a license granting use. + * + * Copyright (C) 2016-2020 OX Software GmbH + * Mail: info@open-xchange.com + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public License 2.0 + * for more details. + */ + +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:ox_coi/src/data/config.dart'; +import 'package:ox_coi/src/settings/settings_manual_form_event_state.dart'; + +class SettingsManualFormBloc extends Bloc { + @override + SettingsManualFormState get initialState => SettingsManualFormStateInitial(); + + @override + Stream mapEventToState(SettingsManualFormEvent event) async* { + if (event is SetupSettings) { + setupSettings(event.shouldLoadConfig, event.email, event.password); + } else if (event is SettingsPrefilled) { + if (event.containsConfig) { + yield SettingsManualFormStateReady( + email: event.email, + imapLogin: event.imapLogin, + imapServer: event.imapServer, + imapPort: event.imapPort, + smtpLogin: event.smtpLogin, + smtpServer: event.smtpServer, + smtpPort: event.smtpPort, + imapSecurity: event.imapSecurity, + smtpSecurity: event.smtpSecurity, + ); + } else { + yield SettingsManualFormStateReady( + email: event.email, + password: event.password, + ); + } + } else if (event is RequestValidateSettings) { + yield SettingsManualFormStateValidation(); + } else if (event is ValidationDone) { + if (event.success) { + yield SettingsManualFormStateValidationSuccess( + email: event.email, + password: event.password, + imapServer: event.imapServer, + imapPort: event.imapPort, + imapSecurity: event.imapSecurity, + smtpServer: event.smtpServer, + smtpPort: event.smtpPort, + smtpSecurity: event.smtpSecurity, + ); + } else { + yield SettingsManualFormStateValidationFailure(); + } + } + } + + void setupSettings(bool shouldLoadConfig, String email, String password) { + if (shouldLoadConfig) { + var config = Config(); + var imapPort = config.imapPort; + var smtpPort = config.smtpPort; + dispatch(SettingsPrefilled( + containsConfig: true, + email: config.email, + imapLogin: config.imapLogin, + imapServer: config.imapServer, + imapPort: imapPort == "0" ? "" : imapPort, + smtpLogin: config.smtpLogin, + smtpServer: config.smtpServer, + smtpPort: smtpPort == "0" ? "" : smtpPort, + imapSecurity: config.imapSecurity, + smtpSecurity: config.smtpSecurity, + )); + } else { + dispatch(SettingsPrefilled(containsConfig: false, email: email, password: password)); + } + } +} diff --git a/lib/src/settings/settings_manual_form_event_state.dart b/lib/src/settings/settings_manual_form_event_state.dart new file mode 100644 index 00000000..cbb504a8 --- /dev/null +++ b/lib/src/settings/settings_manual_form_event_state.dart @@ -0,0 +1,171 @@ +/* + * OPEN-XCHANGE legal information + * + * All intellectual property rights in the Software are protected by + * international copyright laws. + * + * + * In some countries OX, OX Open-Xchange and open xchange + * as well as the corresponding Logos OX Open-Xchange and OX are registered + * trademarks of the OX Software GmbH group of companies. + * The use of the Logos is not covered by the Mozilla Public License 2.0 (MPL 2.0). + * Instead, you are allowed to use these Logos according to the terms and + * conditions of the Creative Commons License, Version 2.5, Attribution, + * Non-commercial, ShareAlike, and the interpretation of the term + * Non-commercial applicable to the aforementioned license is published + * on the web site https://www.open-xchange.com/terms-and-conditions/. + * + * Please make sure that third-party modules and libraries are used + * according to their respective licenses. + * + * Any modifications to this package must retain all copyright notices + * of the original copyright holder(s) for the original code used. + * + * After any such modifications, the original and derivative code shall remain + * under the copyright of the copyright holder(s) and/or original author(s) as stated here: + * https://www.open-xchange.com/legal/. The contributing author shall be + * given Attribution for the derivative code and a license granting use. + * + * Copyright (C) 2016-2020 OX Software GmbH + * Mail: info@open-xchange.com + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public License 2.0 + * for more details. + */ + +import 'package:flutter/foundation.dart'; + +abstract class SettingsManualFormEvent {} + +class SetupSettings extends SettingsManualFormEvent { + final bool shouldLoadConfig; + final String email; + final String password; + + SetupSettings({@required this.shouldLoadConfig, this.email, this.password}); +} + +class SettingsPrefilled extends SettingsManualFormEvent { + final bool containsConfig; + final String email; + final String imapLogin; + final String imapServer; + final String imapPort; + final String smtpLogin; + final String smtpServer; + final String smtpPort; + final int imapSecurity; + final int smtpSecurity; + final String password; + + SettingsPrefilled({ + @required this.containsConfig, + this.email, + this.imapLogin, + this.imapServer, + this.imapPort, + this.smtpLogin, + this.smtpServer, + this.smtpPort, + this.imapSecurity, + this.smtpSecurity, + this.password, + }); +} + +class RequestValidateSettings extends SettingsManualFormEvent {} + +class ValidationDone extends SettingsManualFormEvent { + final bool success; + final String email; + final String imapLogin; + final String imapServer; + final String imapPort; + final String smtpLogin; + final String smtpServer; + final String smtpPort; + final int imapSecurity; + final int smtpSecurity; + final String password; + + ValidationDone({ + @required this.success, + this.email, + this.imapLogin, + this.imapServer, + this.imapPort, + this.smtpLogin, + this.smtpServer, + this.smtpPort, + this.imapSecurity, + this.smtpSecurity, + this.password, + }); +} + +abstract class SettingsManualFormState {} + +class SettingsManualFormStateInitial extends SettingsManualFormState {} + +class SettingsManualFormStateReady extends SettingsManualFormState { + final String email; + final String password; + final String imapLogin; + final String imapServer; + final String imapPort; + final String smtpLogin; + final String smtpServer; + final String smtpPort; + final int imapSecurity; + final int smtpSecurity; + + SettingsManualFormStateReady({ + this.email, + this.password, + this.imapLogin, + this.imapServer, + this.imapPort, + this.smtpLogin, + this.smtpServer, + this.smtpPort, + this.imapSecurity, + this.smtpSecurity, + }); +} + +class SettingsManualFormStateValidation extends SettingsManualFormState {} + +class SettingsManualFormStateValidationSuccess extends SettingsManualFormState { + final String email; + final String password; + final String imapLogin; + final String imapServer; + final String imapPort; + final String smtpLogin; + final String smtpServer; + final String smtpPort; + final int imapSecurity; + final int smtpSecurity; + + SettingsManualFormStateValidationSuccess({ + this.email, + this.password, + this.imapLogin, + this.imapServer, + this.imapPort, + this.smtpLogin, + this.smtpServer, + this.smtpPort, + this.imapSecurity, + this.smtpSecurity, + }); +} + +class SettingsManualFormStateValidationFailure extends SettingsManualFormState {} diff --git a/lib/src/settings/settings_manual_mixin.dart b/lib/src/settings/settings_manual_mixin.dart deleted file mode 100644 index ca3897da..00000000 --- a/lib/src/settings/settings_manual_mixin.dart +++ /dev/null @@ -1,226 +0,0 @@ -/* - * OPEN-XCHANGE legal information - * - * All intellectual property rights in the Software are protected by - * international copyright laws. - * - * - * In some countries OX, OX Open-Xchange and open xchange - * as well as the corresponding Logos OX Open-Xchange and OX are registered - * trademarks of the OX Software GmbH group of companies. - * The use of the Logos is not covered by the Mozilla Public License 2.0 (MPL 2.0). - * Instead, you are allowed to use these Logos according to the terms and - * conditions of the Creative Commons License, Version 2.5, Attribution, - * Non-commercial, ShareAlike, and the interpretation of the term - * Non-commercial applicable to the aforementioned license is published - * on the web site https://www.open-xchange.com/terms-and-conditions/. - * - * Please make sure that third-party modules and libraries are used - * according to their respective licenses. - * - * Any modifications to this package must retain all copyright notices - * of the original copyright holder(s) for the original code used. - * - * After any such modifications, the original and derivative code shall remain - * under the copyright of the copyright holder(s) and/or original author(s) as stated here: - * https://www.open-xchange.com/legal/. The contributing author shall be - * given Attribution for the derivative code and a license granting use. - * - * Copyright (C) 2016-2020 OX Software GmbH - * Mail: info@open-xchange.com - * - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public License 2.0 - * for more details. - */ - -import 'package:flutter/material.dart'; -import 'package:ox_coi/src/l10n/localizations.dart'; -import 'package:ox_coi/src/ui/dimensions.dart'; -import 'package:ox_coi/src/ui/text_styles.dart'; -import 'package:ox_coi/src/widgets/validatable_text_form_field.dart'; - -mixin ManualSettings { - ValidatableTextFormField enabledEmailField = ValidatableTextFormField( - (context) => AppLocalizations.of(context).emailAddress, - textFormType: TextFormType.email, - inputType: TextInputType.emailAddress, - needValidation: true, - validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidEmail, - ); - ValidatableTextFormField disabledEmailField = ValidatableTextFormField( - (context) => AppLocalizations.of(context).emailAddress, - textFormType: TextFormType.email, - inputType: TextInputType.emailAddress, - needValidation: true, - enabled: false, - validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidEmail, - ); - ValidatableTextFormField passwordField = ValidatableTextFormField( - (context) => AppLocalizations.of(context).password, - textFormType: TextFormType.password, - needValidation: true, - validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidPassword, - ); - ValidatableTextFormField imapLoginNameField = ValidatableTextFormField((context) => AppLocalizations.of(context).loginLabelImapName); - ValidatableTextFormField imapServerField = ValidatableTextFormField( - (context) => AppLocalizations.of(context).loginLabelImapServer, - inputType: TextInputType.url, - ); - ValidatableTextFormField imapPortField = ValidatableTextFormField( - (context) => AppLocalizations.of(context).loginLabelImapPort, - textFormType: TextFormType.port, - inputType: TextInputType.number, - needValidation: true, - validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidPort, - ); - ValidatableTextFormField smtpLoginNameField = ValidatableTextFormField((context) => AppLocalizations.of(context).loginLabelSmtpName); - ValidatableTextFormField smtpPasswordField = ValidatableTextFormField( - (context) => AppLocalizations.of(context).loginLabelSmtpPassword, - textFormType: TextFormType.password, - needValidation: true, - validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidPassword, - ); - ValidatableTextFormField smtpServerField = ValidatableTextFormField( - (context) => AppLocalizations.of(context).loginLabelSmtpServer, - inputType: TextInputType.url, - ); - ValidatableTextFormField smtpPortField = ValidatableTextFormField( - (context) => AppLocalizations.of(context).loginLabelSmtpPort, - textFormType: TextFormType.port, - inputType: TextInputType.number, - needValidation: true, - validationHint: (context) => AppLocalizations.of(context).validatableTextFormFieldHintInvalidPort, - ); - - final formKey = GlobalKey(); - String selectedImapSecurity; - String selectedSmtpSecurity; - - Widget getFormFields({@required BuildContext context, @required bool isLogin, bool fromError = false}) { - return SingleChildScrollView( - child: Padding( - padding: EdgeInsets.all(loginManualSettingsPadding), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Visibility( - visible: isLogin, - child: Text( - AppLocalizations.of(context).loginManualSettings, - style: Theme.of(context).textTheme.headline, - ), - ), - Visibility( - visible: isLogin, - child: Padding( - padding: EdgeInsets.only(top: loginVerticalPadding8dp), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Visibility( - visible: fromError, - child: Text( - AppLocalizations.of(context).loginManualSettingsErrorInfoText, - ), - ), - Padding(padding: EdgeInsets.all(loginManualSettingsSubTitlePadding)), - Text( - AppLocalizations.of(context).loginManualSettingsInfoText, - textAlign: TextAlign.center, - ), - Padding(padding: EdgeInsets.all(loginManualSettingsSubTitlePadding)), - Text( - AppLocalizations.of(context).loginManualSettingsSecondInfoText, - textAlign: TextAlign.center, - ), - ], - ), - ), - ), - Padding(padding: EdgeInsets.only(top: loginVerticalPadding20dp)), - Container( - child: Form( - key: formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Align( - alignment: Alignment.centerLeft, - child: Text( - AppLocalizations.of(context).loginBaseSettingsTitle, - style: Theme.of(context).textTheme.subhead.merge(primaryW500), - ), - ), - isLogin ? enabledEmailField : disabledEmailField, - passwordField, - Padding(padding: EdgeInsets.all(loginVerticalPadding12dp)), - Align( - alignment: Alignment.centerLeft, - child: Text( - AppLocalizations.of(context).loginServerAddressesTitle, - style: Theme.of(context).textTheme.subhead.merge(primaryW500), - ), - ), - imapServerField, - smtpServerField, - Padding(padding: EdgeInsets.all(loginVerticalPadding12dp)), - Align( - alignment: Alignment.centerLeft, - child: Text( - AppLocalizations.of(context).loginAdvancedImapTitle, - style: Theme.of(context).textTheme.subhead.merge(primaryW500), - ), - ), - imapPortField, - Padding(padding: EdgeInsets.all(loginVerticalPadding12dp)), - Text(AppLocalizations.of(context).loginLabelImapSecurity), - DropdownButton( - value: selectedImapSecurity == null ? AppLocalizations.of(context).automatic : selectedImapSecurity, - items: getSecurityOptions(context), - onChanged: (String newValue) { - selectedImapSecurity = newValue; - }), - Padding(padding: EdgeInsets.all(loginVerticalPadding12dp)), - Align( - alignment: Alignment.centerLeft, - child: Text( - AppLocalizations.of(context).loginAdvancedSmtpTitle, - style: Theme.of(context).textTheme.subhead.merge(primaryW500), - ), - ), - smtpPortField, - Padding(padding: EdgeInsets.all(loginVerticalPadding12dp)), - Text(AppLocalizations.of(context).loginLabelSmtpSecurity), - DropdownButton( - value: selectedSmtpSecurity == null ? AppLocalizations.of(context).automatic : selectedSmtpSecurity, - items: getSecurityOptions(context), - onChanged: (String newValue) { - selectedSmtpSecurity = newValue; - }), - ], - ), - )), - ], - ), - ), - ); - } - - List> getSecurityOptions(BuildContext context) { - return [ - AppLocalizations.of(context).automatic, - AppLocalizations.of(context).sslTls, - AppLocalizations.of(context).startTLS, - AppLocalizations.of(context).off, - ].map>((String value) { - return DropdownMenuItem(value: value, child: Text(value)); - }).toList(); - } -} diff --git a/lib/src/user/user_account_settings.dart b/lib/src/user/user_account_settings.dart index 215635a4..d02167f6 100644 --- a/lib/src/user/user_account_settings.dart +++ b/lib/src/user/user_account_settings.dart @@ -43,17 +43,18 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:ox_coi/src/data/config.dart'; import 'package:ox_coi/src/l10n/localizations.dart'; import 'package:ox_coi/src/login/login_bloc.dart'; import 'package:ox_coi/src/login/login_events_state.dart'; import 'package:ox_coi/src/navigation/navigatable.dart'; import 'package:ox_coi/src/navigation/navigation.dart'; import 'package:ox_coi/src/platform/system.dart'; -import 'package:ox_coi/src/settings/settings_manual_mixin.dart'; +import 'package:ox_coi/src/settings/settings_manual_form.dart'; +import 'package:ox_coi/src/settings/settings_manual_form_bloc.dart'; +import 'package:ox_coi/src/settings/settings_manual_form_event_state.dart'; +import 'package:ox_coi/src/ui/dimensions.dart'; import 'package:ox_coi/src/user/user_change_bloc.dart'; import 'package:ox_coi/src/user/user_change_event_state.dart'; -import 'package:ox_coi/src/utils/core.dart'; import 'package:ox_coi/src/utils/dialog_builder.dart'; import 'package:ox_coi/src/utils/toast.dart'; import 'package:ox_coi/src/widgets/fullscreen_progress.dart'; @@ -64,15 +65,14 @@ class UserAccountSettings extends StatefulWidget { _UserAccountSettingsState createState() => _UserAccountSettingsState(); } -class _UserAccountSettingsState extends State with ManualSettings { +class _UserAccountSettingsState extends State { UserChangeBloc _userChangeBloc = UserChangeBloc(); LoginBloc _loginBloc = LoginBloc(); Navigation navigation = Navigation(); OverlayEntry _progressOverlayEntry; + FullscreenProgress _progress; bool _showedErrorDialog = false; - bool _firstBuild = true; - @override void initState() { super.initState(); @@ -87,17 +87,10 @@ class _UserAccountSettingsState extends State with ManualSe _handleUserChangeStateChange(UserChangeState state) { if (state is UserChangeStateApplied) { - _progressOverlayEntry = OverlayEntry( - builder: (context) => FullscreenProgress( - bloc: _loginBloc, - text: AppLocalizations.of(context).accountSettingsDataProgressMessage, - showProgressValues: true, - showCancelButton: false, - ), - ); - Overlay.of(context).insert(_progressOverlayEntry); _showedErrorDialog = false; _loginBloc.dispatch(EditButtonPressed()); + } else if (state is UserChangeStateFailure) { + showToast(state.error); } } @@ -126,81 +119,78 @@ class _UserAccountSettingsState extends State with ManualSe @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - leading: new IconButton( - icon: new Icon(Icons.close), - onPressed: () => navigation.pop(context), + return BlocProvider( + builder: (context) { + var settingsManualFormBloc = SettingsManualFormBloc(); + settingsManualFormBloc.dispatch(SetupSettings( + shouldLoadConfig: true, + )); + return settingsManualFormBloc; + }, + child: BlocListener( + listener: (BuildContext context, state) { + if (state is SettingsManualFormStateValidationSuccess) { + _progress = FullscreenProgress( + bloc: _loginBloc, + text: AppLocalizations.of(context).loginProgressMessage, + showProgressValues: true, + showCancelButton: false, + ); + _progressOverlayEntry = OverlayEntry(builder: (context) => _progress); + OverlayState overlayState = Overlay.of(context); + overlayState.insert(_progressOverlayEntry); + _userChangeBloc.dispatch( + UserAccountDataChanged( + imapLogin: state.imapLogin, + imapPassword: state.password, + imapServer: state.imapServer, + imapPort: state.imapPort, + imapSecurity: state.imapSecurity, + smtpLogin: state.smtpLogin, + smtpPassword: state.password, + smtpServer: state.smtpServer, + smtpPort: state.smtpPort, + smtpSecurity: state.smtpSecurity, + ), + ); + } + }, + child: Scaffold( + appBar: AppBar( + leading: new IconButton( + icon: new Icon(Icons.close), + onPressed: () => navigation.pop(context), + ), + title: Text(AppLocalizations.of(context).accountSettingsTitle), + actions: [ + SaveDataButton(), + ], + ), + body: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(loginManualSettingsPadding), + child: SettingsManualForm(isLogin: false), + ), ), - title: Text(AppLocalizations.of(context).accountSettingsTitle), - actions: [IconButton(icon: Icon(Icons.check), onPressed: saveAccountData)], ), - body: buildForm()); - } - - Widget buildForm() { - return BlocBuilder( - bloc: _userChangeBloc, - builder: (context, state) { - if (state is UserChangeStateSuccess) { - if (_firstBuild) { - _firstBuild = false; - _fillEditAccountDataView(state.config); - } - return _buildEditAccountDataView(); - } else if (state is UserChangeStateFailure) { - showToast(state.error); - return _buildEditAccountDataView(); - } else if (state is UserChangeStateApplied) { - return _buildEditAccountDataView(); - } else { - return Container(); - } - }); - } - - _fillEditAccountDataView(Config config) { - disabledEmailField.controller.text = config.email; - imapLoginNameField.controller.text = config.imapLogin; - imapServerField.controller.text = config.imapServer; - imapPortField.controller.text = config.imapPort; - smtpLoginNameField.controller.text = config.smtpLogin; - smtpServerField.controller.text = config.imapServer; - smtpPortField.controller.text = config.smtpPort; - selectedImapSecurity = convertProtocolIntToString(context, config.imapSecurity); - selectedSmtpSecurity = convertProtocolIntToString(context, config.smtpSecurity); + ), + ); } +} - Widget _buildEditAccountDataView() { - return getFormFields(context: context, isLogin: false); +class SaveDataButton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return IconButton( + icon: Icon(Icons.check), + onPressed: () { + _saveData(context); + }, + ); } - saveAccountData() { - hideKeyboard(); - if (formKey.currentState.validate()) { - var imapLogin = disabledEmailField.controller.text; - var imapPassword = passwordField.controller.text; - var imapServer = imapServerField.controller.text; - var imapPort = imapPortField.controller.text; - var imapSecurity = convertProtocolStringToInt(context, selectedImapSecurity); - var smtpLogin = smtpLoginNameField.controller.text; - var smtpPassword = smtpPasswordField.controller.text; - var smtpServer = smtpServerField.controller.text; - var smtpPort = smtpPortField.controller.text; - var smtpSecurity = convertProtocolStringToInt(context, selectedSmtpSecurity); - - _userChangeBloc.dispatch(UserAccountDataChanged( - imapLogin: imapLogin, - imapPassword: imapPassword, - imapServer: imapServer, - imapPort: imapPort, - imapSecurity: imapSecurity, - smtpLogin: smtpLogin, - smtpPassword: smtpPassword, - smtpServer: smtpServer, - smtpPort: smtpPort, - smtpSecurity: smtpSecurity, - )); - } + _saveData(BuildContext context) { + unFocus(context); + BlocProvider.of(context).dispatch(RequestValidateSettings()); } } diff --git a/lib/src/widgets/fullscreen_progress.dart b/lib/src/widgets/fullscreen_progress.dart index 144aa3e3..d8af8d4c 100644 --- a/lib/src/widgets/fullscreen_progress.dart +++ b/lib/src/widgets/fullscreen_progress.dart @@ -63,7 +63,7 @@ class FullscreenProgress extends StatelessWidget { bloc: bloc, builder: (BuildContext context, state) { int progress = 0; - if (state is ProgressState) { + if (state is ProgressState && state.progress != null) { progress = state.progress; } return Container( diff --git a/pubspec.yaml b/pubspec.yaml index 28fefeb4..456c73b9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,28 +12,28 @@ dependencies: flutter_localizations: sdk: flutter bloc: ^0.14.4 - contacts_service: ^0.2.1 + contacts_service: ^0.2.8 cupertino_icons: ^0.1.2 date_format: ^1.0.6 downloads_path_provider: ^0.1.0 - firebase_messaging: ^5.0.0 - file_picker: ^1.3.3 - flutter_bloc: ^0.19.1 - flutter_local_notifications: ^0.7.0 - flutter_sound: ^1.3.7 - fluttertoast: ^3.0.4 - image_cropper: ^1.0.0 - image_picker: ^0.5.0+3 + firebase_messaging: ^5.1.1 + file_picker: ^1.3.8 + flutter_bloc: ^0.20.0 + flutter_local_notifications: ^0.7.1+3 + flutter_sound: ^1.4.2 + fluttertoast: ^3.1.0 + image_cropper: ^1.0.2 + image_picker: ^0.6.0+17 logging: ^0.11.3+2 - open_file: ^2.0.1+1 - package_info: ^0.4.0+2 - path_provider: ^0.5.0+1 - permission_handler: ^3.0.0 - rxdart: ^0.22.0 - shared_preferences: ^0.5.1+1 - url_launcher: ^5.0.2 - video_player: ^0.10.0+4 - qr_flutter: ^2.0.0 + open_file: ^2.0.3 + package_info: ^0.4.0+5 + path_provider: ^1.1.2 + permission_handler: ^3.2.0 + rxdart: ^0.22.1 + shared_preferences: ^0.5.3+4 + url_launcher: ^5.1.0 + video_player: ^0.10.1+4 + qr_flutter: ^2.1.0+55 qr_mobile_vision: ^0.2.2 delta_chat_core: path: ../flutter-deltachat-core/ # Intended path outside the project context