From 8cb13ec945e80cc89d7abdb5d231532fe1685700 Mon Sep 17 00:00:00 2001 From: Nic Ford Date: Wed, 18 Dec 2024 17:30:59 +0000 Subject: [PATCH] fix: make ui behave in absence of strategies [CLERK_SDK #65] --- .../src/models/environment/environment.dart | 32 ++++++++++-- .../clerk_authentication_widget.dart | 50 ++++++++++++------- .../authentication/clerk_sign_in_panel.dart | 48 +++++++++--------- .../authentication/clerk_sign_up_panel.dart | 29 +++++------ .../authentication/clerk_sso_panel.dart | 39 ++++++++------- .../lib/src/widgets/ui/clerk_code_input.dart | 4 +- .../widgets/ui/multi_digit_code_input.dart | 4 -- 7 files changed, 117 insertions(+), 89 deletions(-) diff --git a/packages/clerk_auth/lib/src/models/environment/environment.dart b/packages/clerk_auth/lib/src/models/environment/environment.dart index c63e307..214666c 100644 --- a/packages/clerk_auth/lib/src/models/environment/environment.dart +++ b/packages/clerk_auth/lib/src/models/environment/environment.dart @@ -1,10 +1,6 @@ +import 'package:clerk_auth/clerk_auth.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'auth_config.dart'; -import 'display_config.dart'; -import 'organization_settings.dart'; -import 'user_settings.dart'; - part 'environment.g.dart'; /// [Environment] Clerk object @@ -44,6 +40,32 @@ class Environment { /// empty [Environment] static const empty = Environment(); + /// Do we have [Strategy.password] configured? + bool get hasPasswordStrategy => + config.firstFactors.contains(Strategy.password); + + /// [Iterable] of identification strategies + Iterable get identificationStrategies => + config.identificationStrategies.where((i) => i.isOauth == false); + + /// Do we have identification strategies? + bool get hasIdentificationStrategies => identificationStrategies.isNotEmpty; + + /// [Iterable] of oauth strategies + Iterable get oauthStrategies => + config.identificationStrategies.where((i) => i.isOauth); + + /// Do we have oauth strategies? + bool get hasOauthStrategies => oauthStrategies.isNotEmpty; + + /// [Iterable] of other strategies + /// i.e. strategies that are neither oauth nor password-based + Iterable get otherStrategies => + config.firstFactors.where((f) => f.isOtherStrategy); + + /// Do we have other strategies? + bool get hasOtherStrategies => otherStrategies.isNotEmpty; + /// fromJson static Environment fromJson(Map json) => _$EnvironmentFromJson(json); diff --git a/packages/clerk_flutter/lib/src/widgets/authentication/clerk_authentication_widget.dart b/packages/clerk_flutter/lib/src/widgets/authentication/clerk_authentication_widget.dart index d48bc97..f895231 100644 --- a/packages/clerk_flutter/lib/src/widgets/authentication/clerk_authentication_widget.dart +++ b/packages/clerk_flutter/lib/src/widgets/authentication/clerk_authentication_widget.dart @@ -43,25 +43,37 @@ class _ClerkAuthenticationWidgetState extends State { constraints: const BoxConstraints(minHeight: 530.0), child: ClerkVerticalCard( topPortion: _TopPortion(state: _state), - middlePortion: Column( - children: [ - ClerkAuthBuilder( - builder: (context, auth) { - return Closeable( - closed: auth.isSigningIn || auth.isSigningUp, - child: const ClerkSSOPanel(), - ); - }, - ), - Closeable( - closed: _state.isSigningIn == false, - child: const ClerkSignInPanel(), - ), - Closeable( - closed: _state.isSigningUp == false, - child: const ClerkSignUpPanel(), - ), - ], + middlePortion: ClerkAuthBuilder( + builder: (context, auth) { + final env = auth.env; + return Padding( + padding: horizontalPadding32, + child: Column( + children: [ + if (env.hasOauthStrategies) // + Closeable( + closed: auth.isSigningIn || auth.isSigningUp, + child: const ClerkSSOPanel(), + ), + if (env.hasIdentificationStrategies) ...[ + if (env.hasOauthStrategies) // + const Padding( + padding: verticalPadding24, + child: OrDivider(), + ), + Closeable( + closed: _state.isSigningIn == false, + child: const ClerkSignInPanel(), + ), + Closeable( + closed: _state.isSigningUp == false, + child: const ClerkSignUpPanel(), + ), + ], + ], + ), + ); + }, ), bottomPortion: _BottomPortion( state: _state, diff --git a/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sign_in_panel.dart b/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sign_in_panel.dart index f64c487..fd08204 100644 --- a/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sign_in_panel.dart +++ b/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sign_in_panel.dart @@ -63,23 +63,22 @@ class _ClerkSignInPanelState extends State { final auth = ClerkAuth.of(context); final translator = auth.translator; final env = auth.env; - final otherStrategies = - env.config.firstFactors.where((f) => f.isOtherStrategy); - final hasPasswordStrategy = - env.config.firstFactors.contains(clerk.Strategy.password); - final identifiers = env.config.identificationStrategies - .where((i) => i.isOauth == false) + final identifiers = env.identificationStrategies .map((i) => i.toString().replaceAll('_', ' ')); final factor = auth.client.signIn?.supportedFirstFactors .firstWhereOrNull((f) => f.strategy == _strategy); final safeIdentifier = factor?.safeIdentifier; + if (identifiers.isEmpty) { + return emptyWidget; + } + return Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ Padding( - padding: horizontalPadding32 + bottomPadding8, + padding: bottomPadding8, child: ClerkTextFormField( key: const Key('identifier'), label: translator.alternatives(identifiers.toList()).capitalized, @@ -97,28 +96,27 @@ class _ClerkSignInPanelState extends State { Closeable( key: const Key('emailLinkMessage'), closed: _strategy != clerk.Strategy.emailLink, - child: Padding( - padding: horizontalPadding32, - child: Text( - translator.translate( - 'Click on the link that‘s been sent to ### and then check back here', - substitution: _identifier, - ), - maxLines: 2, - style: ClerkTextStyle.inputLabel, + child: Text( + translator.translate( + 'Click on the link that‘s been sent to ### and then check back here', + substitution: _identifier, ), + maxLines: 2, + style: ClerkTextStyle.inputLabel, ), ), Closeable( closed: _strategy.requiresCode == false, child: Padding( - padding: horizontalPadding32 + verticalPadding8, + padding: verticalPadding8, child: ClerkCodeInput( key: const Key('code'), - title: translator.translate( - 'Enter code sent to ###', - substitution: safeIdentifier, - ), + title: safeIdentifier is String + ? translator.translate( + 'Enter the code sent to ###', + substitution: safeIdentifier, + ) + : translator.translate('Enter the code sent to you'), onSubmit: (code) async { await _continue(auth, code: code, strategy: _strategy); return false; @@ -131,7 +129,7 @@ class _ClerkSignInPanelState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (hasPasswordStrategy) + if (env.hasPasswordStrategy) Padding( padding: horizontalPadding32 + verticalPadding8, child: ClerkTextFormField( @@ -142,13 +140,13 @@ class _ClerkSignInPanelState extends State { _continue(auth, strategy: clerk.Strategy.password), ), ), - if (otherStrategies.isNotEmpty) ...[ - if (hasPasswordStrategy) + if (env.hasOtherStrategies) ...[ + if (env.hasPasswordStrategy) const Padding( padding: horizontalPadding32, child: OrDivider(), ), - for (final strategy in otherStrategies) + for (final strategy in env.otherStrategies) if (StrategyButton.supports(strategy)) Padding( padding: topPadding4 + horizontalPadding32, diff --git a/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sign_up_panel.dart b/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sign_up_panel.dart index 5b5582b..e58e291 100644 --- a/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sign_up_panel.dart +++ b/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sign_up_panel.dart @@ -69,7 +69,7 @@ class _ClerkSignUpPanelState extends State { Closeable( closed: auth.signUp?.unverified(clerk.Field.phoneNumber) != true, child: Padding( - padding: horizontalPadding32 + verticalPadding8, + padding: verticalPadding8, child: ClerkCodeInput( key: const Key('phone_code'), title: translator.translate('Verify your phone number'), @@ -91,7 +91,7 @@ class _ClerkSignUpPanelState extends State { Closeable( closed: auth.signUp?.unverified(clerk.Field.emailAddress) != true, child: Padding( - padding: horizontalPadding32 + verticalPadding8, + padding: verticalPadding8, child: ClerkCodeInput( key: const Key('email_code'), title: translator.translate('Verify your email address'), @@ -113,7 +113,7 @@ class _ClerkSignUpPanelState extends State { children: [ for (final attribute in attributes) Padding( - padding: horizontalPadding32 + bottomPadding24, + padding: bottomPadding24, child: attribute.isPhoneNumber ? ClerkPhoneNumberFormField( initial: _values[attribute.attr], @@ -138,19 +138,16 @@ class _ClerkSignUpPanelState extends State { ], ), ), - Padding( - padding: horizontalPadding32, - child: ClerkMaterialButton( - onPressed: () => _continue(auth), - label: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center(child: Text(translator.translate('Continue'))), - horizontalMargin4, - const Icon(Icons.arrow_right_sharp), - ], - ), + ClerkMaterialButton( + onPressed: () => _continue(auth), + label: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center(child: Text(translator.translate('Continue'))), + horizontalMargin4, + const Icon(Icons.arrow_right_sharp), + ], ), ), verticalMargin32, diff --git a/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sso_panel.dart b/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sso_panel.dart index 2324df1..e647f6e 100644 --- a/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sso_panel.dart +++ b/packages/clerk_flutter/lib/src/widgets/authentication/clerk_sso_panel.dart @@ -20,6 +20,13 @@ class ClerkSSOPanel extends StatefulWidget { State createState() => _ClerkSSOPanelState(); } +Widget _buttonFor(clerk.SocialConnection connection) { + return SocialConnectionButton( + key: ValueKey(connection), + connection: connection, + ); +} + class _ClerkSSOPanelState extends State { @override Widget build(BuildContext context) { @@ -30,27 +37,21 @@ class _ClerkSSOPanelState extends State { final socialConnections = env.user.socialSettings.values.where( (s) => oauthStrategies.contains(s.strategy), ); - return Column( + + if (socialConnections.isEmpty) { + return emptyWidget; + } + + return Row( children: [ - Padding( - padding: horizontalPadding32 + bottomPadding24, - child: Row( - children: [ - for (final connection in socialConnections) // - Expanded( - child: Padding( - padding: const EdgeInsets.all(4), - child: SocialConnectionButton( - key: ValueKey(connection), - connection: connection, - ), - ), - ), - ], + Expanded(child: _buttonFor(socialConnections.first)), + for (final connection in socialConnections.skip(1)) // + Expanded( + child: Padding( + padding: leftPadding8, + child: _buttonFor(connection), + ), ), - ), - const OrDivider(), - verticalMargin24, ], ); } diff --git a/packages/clerk_flutter/lib/src/widgets/ui/clerk_code_input.dart b/packages/clerk_flutter/lib/src/widgets/ui/clerk_code_input.dart index 29ed3d0..a185acc 100644 --- a/packages/clerk_flutter/lib/src/widgets/ui/clerk_code_input.dart +++ b/packages/clerk_flutter/lib/src/widgets/ui/clerk_code_input.dart @@ -35,7 +35,9 @@ class ClerkCodeInput extends StatelessWidget { title, textAlign: TextAlign.start, maxLines: 2, - style: ClerkTextStyle.title, + style: subtitle is String + ? ClerkTextStyle.title + : ClerkTextStyle.subtitleDark, ), ), if (subtitle case String subtitle) diff --git a/packages/clerk_flutter/lib/src/widgets/ui/multi_digit_code_input.dart b/packages/clerk_flutter/lib/src/widgets/ui/multi_digit_code_input.dart index a5c2a58..fc906be 100644 --- a/packages/clerk_flutter/lib/src/widgets/ui/multi_digit_code_input.dart +++ b/packages/clerk_flutter/lib/src/widgets/ui/multi_digit_code_input.dart @@ -146,10 +146,6 @@ class _MultiDigitCodeInputState extends State const color = ClerkColors.midGrey; bool isSmall = widget.isSmall; - if (isSmall == false) { - final viewQuery = MediaQueryData.fromView(View.of(context)); - viewQuery.size.height - viewQuery.viewInsets.bottom < 400.0; - } final boxSize = isSmall ? 18.0 : 38.0; final cursorHeight = isSmall ? 1.0 : 2.0;