diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart index d6bc61901..a9d411714 100644 --- a/lib/pages/login/login.dart +++ b/lib/pages/login/login.dart @@ -72,6 +72,16 @@ class LoginController extends State { }); } + @override + void dispose() { + usernameController.dispose(); + passwordController.dispose(); + loading = false; + usernameError = null; + passwordError = null; + super.dispose(); + } + void _setStateOnTextChange(String? oldText, String newText) { if ((oldText == null || oldText.isEmpty) && (newText.isNotEmpty)) { setState(() {}); diff --git a/lib/pangea/pages/connect/p_sso_button.dart b/lib/pangea/pages/connect/p_sso_button.dart index 148b78301..553fef632 100644 --- a/lib/pangea/pages/connect/p_sso_button.dart +++ b/lib/pangea/pages/connect/p_sso_button.dart @@ -55,6 +55,30 @@ class PangeaSsoButton extends StatefulWidget { class PangeaSsoButtonState extends State { bool _loading = false; String? _error; + AppLifecycleListener? _listener; + + @override + void initState() { + _listener = AppLifecycleListener( + onStateChange: _onAppLifecycleChange, + ); + super.initState(); + } + + // when the SSO login launches, stop the loading indicator + void _onAppLifecycleChange(AppLifecycleState state) { + if ((state == AppLifecycleState.inactive || + state == AppLifecycleState.hidden) && + _loading) { + setState(() => _loading = false); + } + } + + @override + void dispose() { + _listener?.dispose(); + super.dispose(); + } Future _runSSOLogin() async { try { diff --git a/lib/pangea/pages/sign_up/full_width_button.dart b/lib/pangea/pages/sign_up/full_width_button.dart index a1ba036fb..d3d156338 100644 --- a/lib/pangea/pages/sign_up/full_width_button.dart +++ b/lib/pangea/pages/sign_up/full_width_button.dart @@ -43,6 +43,7 @@ class FullWidthButtonState extends State { onPressed: widget.onPressed, borderRadius: BorderRadius.circular(36), color: Theme.of(context).colorScheme.primary, + isShadow: true, child: Container( // internal padding padding: const EdgeInsets.symmetric(horizontal: 16), diff --git a/lib/pangea/pages/sign_up/signup.dart b/lib/pangea/pages/sign_up/signup.dart index e9d09f160..89ec62537 100644 --- a/lib/pangea/pages/sign_up/signup.dart +++ b/lib/pangea/pages/sign_up/signup.dart @@ -55,6 +55,16 @@ class SignupPageController extends State { }); } + @override + void dispose() { + usernameController.dispose(); + passwordController.dispose(); + emailController.dispose(); + loading = false; + error = null; + super.dispose(); + } + bool get enableSignUp => !loading && isTnCChecked && diff --git a/lib/pangea/pages/sign_up/signup_with_email_view.dart b/lib/pangea/pages/sign_up/signup_with_email_view.dart index 055624017..087d8ea1a 100644 --- a/lib/pangea/pages/sign_up/signup_with_email_view.dart +++ b/lib/pangea/pages/sign_up/signup_with_email_view.dart @@ -44,7 +44,11 @@ class SignupWithEmailView extends StatelessWidget { validator: controller.password1TextFieldValidator, controller: controller.passwordController, ), - TosCheckbox(controller), + TosCheckbox( + controller.isTnCChecked, + controller.onTncChange, + error: controller.signupError, + ), FullWidthButton( title: L10n.of(context).signUp, icon: PangeaLogoSvg( diff --git a/lib/pangea/pages/sign_up/user_settings.dart b/lib/pangea/pages/sign_up/user_settings.dart index e8d0ca48c..6d29cdf2c 100644 --- a/lib/pangea/pages/sign_up/user_settings.dart +++ b/lib/pangea/pages/sign_up/user_settings.dart @@ -29,12 +29,15 @@ class UserSettingsState extends State { String? selectedLanguageError; String? profileCreationError; + String? tncError; bool loading = false; Uint8List? avatar; String? _selectedFilePath; + bool isTncChecked = false; + List avatarPaths = const [ "assets/pangea/Avatar_1.png", "assets/pangea/Avatar_2.png", @@ -52,7 +55,6 @@ class UserSettingsState extends State { : PangeaLanguage.byLangCode(systemLangCode); } - bool canSetDisplayName = false; TextEditingController displayNameController = TextEditingController(); final GlobalKey formKey = GlobalKey(); @@ -61,19 +63,31 @@ class UserSettingsState extends State { super.initState(); selectedTargetLanguage = _pangeaController.languageController.userL2; selectedAvatarPath = avatarPaths.first; - final loginTypeEntry = - _pangeaController.pStoreService.read(PLocalKey.loginType); - if (loginTypeEntry is String && loginTypeEntry == 'sso') { - canSetDisplayName = true; - } } @override void dispose() { displayNameController.dispose(); + loading = false; + selectedLanguageError = null; + profileCreationError = null; + tncError = null; super.dispose(); } + bool get isSSOSignup { + final loginTypeEntry = + _pangeaController.pStoreService.read(PLocalKey.loginType); + return loginTypeEntry is String && loginTypeEntry == 'sso'; + } + + void setTncChecked(bool? value) { + setState(() { + isTncChecked = value ?? false; + tncError = null; + }); + } + void setSelectedTargetLanguage(LanguageModel? language) { setState(() { selectedTargetLanguage = language; @@ -150,7 +164,11 @@ class UserSettingsState extends State { } Future createUserInPangea() async { - setState(() => profileCreationError = null); + setState(() { + profileCreationError = null; + selectedLanguageError = null; + tncError = null; + }); if (selectedTargetLanguage == null) { setState(() { @@ -159,6 +177,13 @@ class UserSettingsState extends State { return; } + if (isSSOSignup && !isTncChecked) { + setState(() { + tncError = L10n.of(context).pleaseAgreeToTOS; + }); + return; + } + if (!formKey.currentState!.validate()) return; setState(() => loading = true); diff --git a/lib/pangea/pages/sign_up/user_settings_view.dart b/lib/pangea/pages/sign_up/user_settings_view.dart index bac7b8efc..dbb92a20c 100644 --- a/lib/pangea/pages/sign_up/user_settings_view.dart +++ b/lib/pangea/pages/sign_up/user_settings_view.dart @@ -3,6 +3,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/pages/sign_up/full_width_button.dart'; import 'package:fluffychat/pangea/pages/sign_up/pangea_login_scaffold.dart'; import 'package:fluffychat/pangea/pages/sign_up/user_settings.dart'; +import 'package:fluffychat/pangea/widgets/signup/tos_checkbox.dart'; import 'package:fluffychat/pangea/widgets/user_settings/p_language_dropdown.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -93,7 +94,7 @@ class UserSettingsView extends StatelessWidget { error: controller.selectedLanguageError, ), ), - if (controller.canSetDisplayName) + if (controller.isSSOSignup) FullWidthTextField( hintText: L10n.of(context).username, validator: (username) { @@ -104,6 +105,12 @@ class UserSettingsView extends StatelessWidget { }, controller: controller.displayNameController, ), + if (controller.isSSOSignup) + TosCheckbox( + controller.isTncChecked, + controller.setTncChecked, + error: controller.tncError, + ), FullWidthButton( title: L10n.of(context).letsStart, onPressed: controller.selectedTargetLanguage != null @@ -111,7 +118,8 @@ class UserSettingsView extends StatelessWidget { : null, error: controller.profileCreationError, loading: controller.loading, - enabled: controller.selectedTargetLanguage != null, + enabled: controller.selectedTargetLanguage != null && + (!controller.isSSOSignup || controller.isTncChecked), ), ], ), diff --git a/lib/pangea/widgets/pressable_button.dart b/lib/pangea/widgets/pressable_button.dart index a6bf4fa46..97e78f69f 100644 --- a/lib/pangea/widgets/pressable_button.dart +++ b/lib/pangea/widgets/pressable_button.dart @@ -16,6 +16,8 @@ class PressableButton extends StatefulWidget { final Stream? triggerAnimation; final ClickPlayer? clickPlayer; + final bool? isShadow; + const PressableButton({ required this.borderRadius, required this.child, @@ -25,6 +27,7 @@ class PressableButton extends StatefulWidget { this.depressed = false, this.triggerAnimation, this.clickPlayer, + this.isShadow, super.key, }); @@ -53,6 +56,7 @@ class PressableButtonState extends State ); _tweenAnimation = Tween(begin: 0, end: widget.buttonHeight).animate(_controller); + if (!_depressed) { _triggerAnimationSubscription = widget.triggerAnimation?.listen((_) { _animationCompleter = Completer(); @@ -77,6 +81,9 @@ class PressableButtonState extends State } } + bool get _isShadow => + widget.isShadow ?? Theme.of(context).brightness == Brightness.light; + void _onTapDown(TapDownDetails? details) { if (_depressed) return; _animationCompleter = Completer(); @@ -138,7 +145,7 @@ class PressableButtonState extends State return Container( decoration: BoxDecoration( color: Color.alphaBlend( - Theme.of(context).brightness == Brightness.light + _isShadow ? Colors.black.withOpacity(0.25) : Colors.white.withOpacity(0.25), widget.color, diff --git a/lib/pangea/widgets/signup/tos_checkbox.dart b/lib/pangea/widgets/signup/tos_checkbox.dart index f7e58ab0b..031808aa1 100644 --- a/lib/pangea/widgets/signup/tos_checkbox.dart +++ b/lib/pangea/widgets/signup/tos_checkbox.dart @@ -2,16 +2,19 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/pangea/pages/sign_up/signup.dart'; import 'package:fluffychat/utils/url_launcher.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; class TosCheckbox extends StatefulWidget { - final SignupPageController controller; + final bool value; + final Function(bool?) onChange; + final String? error; const TosCheckbox( - this.controller, { + this.value, + this.onChange, { + this.error, super.key, }); @@ -57,12 +60,12 @@ class TosCheckboxState extends State ), AnimatedSize( duration: FluffyThemes.animationDuration, - child: widget.controller.signupError == null + child: widget.error == null ? const SizedBox.shrink() : Padding( padding: const EdgeInsets.only(top: 4, left: 30), child: Text( - widget.controller.signupError!, + widget.error!, style: TextStyle( color: Theme.of(context).colorScheme.error, fontSize: 12, @@ -74,9 +77,9 @@ class TosCheckboxState extends State ), ), Checkbox( - value: widget.controller.isTnCChecked, + value: widget.value, activeColor: Theme.of(context).colorScheme.primary, - onChanged: widget.controller.onTncChange, + onChanged: widget.onChange, ), ], );