Signup login updates (#1324)

* fix: stop signup/login loading on dispose

* fix: if user signs up with SSO, make them agree to TOS on language/avatar settings page
pull/1544/head
ggurdin 10 months ago committed by GitHub
parent 6b24b68239
commit 0368ce3ea7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -72,6 +72,16 @@ class LoginController extends State<Login> {
}); });
} }
@override
void dispose() {
usernameController.dispose();
passwordController.dispose();
loading = false;
usernameError = null;
passwordError = null;
super.dispose();
}
void _setStateOnTextChange(String? oldText, String newText) { void _setStateOnTextChange(String? oldText, String newText) {
if ((oldText == null || oldText.isEmpty) && (newText.isNotEmpty)) { if ((oldText == null || oldText.isEmpty) && (newText.isNotEmpty)) {
setState(() {}); setState(() {});

@ -55,6 +55,30 @@ class PangeaSsoButton extends StatefulWidget {
class PangeaSsoButtonState extends State<PangeaSsoButton> { class PangeaSsoButtonState extends State<PangeaSsoButton> {
bool _loading = false; bool _loading = false;
String? _error; 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<void> _runSSOLogin() async { Future<void> _runSSOLogin() async {
try { try {

@ -43,6 +43,7 @@ class FullWidthButtonState extends State<FullWidthButton> {
onPressed: widget.onPressed, onPressed: widget.onPressed,
borderRadius: BorderRadius.circular(36), borderRadius: BorderRadius.circular(36),
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
isShadow: true,
child: Container( child: Container(
// internal padding // internal padding
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),

@ -55,6 +55,16 @@ class SignupPageController extends State<SignupPage> {
}); });
} }
@override
void dispose() {
usernameController.dispose();
passwordController.dispose();
emailController.dispose();
loading = false;
error = null;
super.dispose();
}
bool get enableSignUp => bool get enableSignUp =>
!loading && !loading &&
isTnCChecked && isTnCChecked &&

@ -44,7 +44,11 @@ class SignupWithEmailView extends StatelessWidget {
validator: controller.password1TextFieldValidator, validator: controller.password1TextFieldValidator,
controller: controller.passwordController, controller: controller.passwordController,
), ),
TosCheckbox(controller), TosCheckbox(
controller.isTnCChecked,
controller.onTncChange,
error: controller.signupError,
),
FullWidthButton( FullWidthButton(
title: L10n.of(context).signUp, title: L10n.of(context).signUp,
icon: PangeaLogoSvg( icon: PangeaLogoSvg(

@ -29,12 +29,15 @@ class UserSettingsState extends State<UserSettingsPage> {
String? selectedLanguageError; String? selectedLanguageError;
String? profileCreationError; String? profileCreationError;
String? tncError;
bool loading = false; bool loading = false;
Uint8List? avatar; Uint8List? avatar;
String? _selectedFilePath; String? _selectedFilePath;
bool isTncChecked = false;
List<String> avatarPaths = const [ List<String> avatarPaths = const [
"assets/pangea/Avatar_1.png", "assets/pangea/Avatar_1.png",
"assets/pangea/Avatar_2.png", "assets/pangea/Avatar_2.png",
@ -52,7 +55,6 @@ class UserSettingsState extends State<UserSettingsPage> {
: PangeaLanguage.byLangCode(systemLangCode); : PangeaLanguage.byLangCode(systemLangCode);
} }
bool canSetDisplayName = false;
TextEditingController displayNameController = TextEditingController(); TextEditingController displayNameController = TextEditingController();
final GlobalKey<FormState> formKey = GlobalKey<FormState>(); final GlobalKey<FormState> formKey = GlobalKey<FormState>();
@ -61,19 +63,31 @@ class UserSettingsState extends State<UserSettingsPage> {
super.initState(); super.initState();
selectedTargetLanguage = _pangeaController.languageController.userL2; selectedTargetLanguage = _pangeaController.languageController.userL2;
selectedAvatarPath = avatarPaths.first; selectedAvatarPath = avatarPaths.first;
final loginTypeEntry =
_pangeaController.pStoreService.read(PLocalKey.loginType);
if (loginTypeEntry is String && loginTypeEntry == 'sso') {
canSetDisplayName = true;
}
} }
@override @override
void dispose() { void dispose() {
displayNameController.dispose(); displayNameController.dispose();
loading = false;
selectedLanguageError = null;
profileCreationError = null;
tncError = null;
super.dispose(); 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) { void setSelectedTargetLanguage(LanguageModel? language) {
setState(() { setState(() {
selectedTargetLanguage = language; selectedTargetLanguage = language;
@ -150,7 +164,11 @@ class UserSettingsState extends State<UserSettingsPage> {
} }
Future<void> createUserInPangea() async { Future<void> createUserInPangea() async {
setState(() => profileCreationError = null); setState(() {
profileCreationError = null;
selectedLanguageError = null;
tncError = null;
});
if (selectedTargetLanguage == null) { if (selectedTargetLanguage == null) {
setState(() { setState(() {
@ -159,6 +177,13 @@ class UserSettingsState extends State<UserSettingsPage> {
return; return;
} }
if (isSSOSignup && !isTncChecked) {
setState(() {
tncError = L10n.of(context).pleaseAgreeToTOS;
});
return;
}
if (!formKey.currentState!.validate()) return; if (!formKey.currentState!.validate()) return;
setState(() => loading = true); setState(() => loading = true);

@ -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/full_width_button.dart';
import 'package:fluffychat/pangea/pages/sign_up/pangea_login_scaffold.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/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:fluffychat/pangea/widgets/user_settings/p_language_dropdown.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -93,7 +94,7 @@ class UserSettingsView extends StatelessWidget {
error: controller.selectedLanguageError, error: controller.selectedLanguageError,
), ),
), ),
if (controller.canSetDisplayName) if (controller.isSSOSignup)
FullWidthTextField( FullWidthTextField(
hintText: L10n.of(context).username, hintText: L10n.of(context).username,
validator: (username) { validator: (username) {
@ -104,6 +105,12 @@ class UserSettingsView extends StatelessWidget {
}, },
controller: controller.displayNameController, controller: controller.displayNameController,
), ),
if (controller.isSSOSignup)
TosCheckbox(
controller.isTncChecked,
controller.setTncChecked,
error: controller.tncError,
),
FullWidthButton( FullWidthButton(
title: L10n.of(context).letsStart, title: L10n.of(context).letsStart,
onPressed: controller.selectedTargetLanguage != null onPressed: controller.selectedTargetLanguage != null
@ -111,7 +118,8 @@ class UserSettingsView extends StatelessWidget {
: null, : null,
error: controller.profileCreationError, error: controller.profileCreationError,
loading: controller.loading, loading: controller.loading,
enabled: controller.selectedTargetLanguage != null, enabled: controller.selectedTargetLanguage != null &&
(!controller.isSSOSignup || controller.isTncChecked),
), ),
], ],
), ),

@ -16,6 +16,8 @@ class PressableButton extends StatefulWidget {
final Stream? triggerAnimation; final Stream? triggerAnimation;
final ClickPlayer? clickPlayer; final ClickPlayer? clickPlayer;
final bool? isShadow;
const PressableButton({ const PressableButton({
required this.borderRadius, required this.borderRadius,
required this.child, required this.child,
@ -25,6 +27,7 @@ class PressableButton extends StatefulWidget {
this.depressed = false, this.depressed = false,
this.triggerAnimation, this.triggerAnimation,
this.clickPlayer, this.clickPlayer,
this.isShadow,
super.key, super.key,
}); });
@ -53,6 +56,7 @@ class PressableButtonState extends State<PressableButton>
); );
_tweenAnimation = _tweenAnimation =
Tween<double>(begin: 0, end: widget.buttonHeight).animate(_controller); Tween<double>(begin: 0, end: widget.buttonHeight).animate(_controller);
if (!_depressed) { if (!_depressed) {
_triggerAnimationSubscription = widget.triggerAnimation?.listen((_) { _triggerAnimationSubscription = widget.triggerAnimation?.listen((_) {
_animationCompleter = Completer<void>(); _animationCompleter = Completer<void>();
@ -77,6 +81,9 @@ class PressableButtonState extends State<PressableButton>
} }
} }
bool get _isShadow =>
widget.isShadow ?? Theme.of(context).brightness == Brightness.light;
void _onTapDown(TapDownDetails? details) { void _onTapDown(TapDownDetails? details) {
if (_depressed) return; if (_depressed) return;
_animationCompleter = Completer<void>(); _animationCompleter = Completer<void>();
@ -138,7 +145,7 @@ class PressableButtonState extends State<PressableButton>
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Color.alphaBlend( color: Color.alphaBlend(
Theme.of(context).brightness == Brightness.light _isShadow
? Colors.black.withOpacity(0.25) ? Colors.black.withOpacity(0.25)
: Colors.white.withOpacity(0.25), : Colors.white.withOpacity(0.25),
widget.color, widget.color,

@ -2,16 +2,19 @@
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.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:fluffychat/utils/url_launcher.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
class TosCheckbox extends StatefulWidget { class TosCheckbox extends StatefulWidget {
final SignupPageController controller; final bool value;
final Function(bool?) onChange;
final String? error;
const TosCheckbox( const TosCheckbox(
this.controller, { this.value,
this.onChange, {
this.error,
super.key, super.key,
}); });
@ -57,12 +60,12 @@ class TosCheckboxState extends State<TosCheckbox>
), ),
AnimatedSize( AnimatedSize(
duration: FluffyThemes.animationDuration, duration: FluffyThemes.animationDuration,
child: widget.controller.signupError == null child: widget.error == null
? const SizedBox.shrink() ? const SizedBox.shrink()
: Padding( : Padding(
padding: const EdgeInsets.only(top: 4, left: 30), padding: const EdgeInsets.only(top: 4, left: 30),
child: Text( child: Text(
widget.controller.signupError!, widget.error!,
style: TextStyle( style: TextStyle(
color: Theme.of(context).colorScheme.error, color: Theme.of(context).colorScheme.error,
fontSize: 12, fontSize: 12,
@ -74,9 +77,9 @@ class TosCheckboxState extends State<TosCheckbox>
), ),
), ),
Checkbox( Checkbox(
value: widget.controller.isTnCChecked, value: widget.value,
activeColor: Theme.of(context).colorScheme.primary, activeColor: Theme.of(context).colorScheme.primary,
onChanged: widget.controller.onTncChange, onChanged: widget.onChange,
), ),
], ],
); );

Loading…
Cancel
Save