Skip to content

Commit 721fff0

Browse files
committed
feat: otp improvement on mobile (AppFlowy-IO#7738)
1 parent 1bda0d0 commit 721fff0

File tree

12 files changed

+93
-53
lines changed

12 files changed

+93
-53
lines changed

frontend/appflowy_flutter/lib/mobile/presentation/setting/user_session_setting_group.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,15 @@ class UserSessionSettingGroup extends StatelessWidget {
6363
);
6464
},
6565
builder: (context, state) {
66-
return const ThirdPartySignInButtons(
67-
expanded: true,
66+
return Column(
67+
children: [
68+
const ContinueWithEmailAndPassword(),
69+
const VSpace(12.0),
70+
const ThirdPartySignInButtons(
71+
expanded: true,
72+
),
73+
const VSpace(16.0),
74+
],
6875
);
6976
},
7077
),

frontend/appflowy_flutter/lib/user/application/sign_in_bloc.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,13 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
186186
Emitter<SignInState> emit, {
187187
required String email,
188188
}) async {
189+
if (state.isSubmitting) {
190+
Log.error('Sign in with magic link is already in progress');
191+
return;
192+
}
193+
194+
Log.info('Sign in with magic link: $email');
195+
189196
emit(
190197
state.copyWith(
191198
isSubmitting: true,

frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/desktop_sign_in_screen.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
55
import 'package:appflowy/shared/settings/show_settings.dart';
66
import 'package:appflowy/shared/window_title_bar.dart';
77
import 'package:appflowy/user/application/sign_in_bloc.dart';
8+
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/anonymous_sign_in_button.dart';
89
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
910
import 'package:appflowy/user/presentation/widgets/widgets.dart';
1011
import 'package:appflowy_ui/appflowy_ui.dart';
@@ -25,6 +26,7 @@ class DesktopSignInScreen extends StatelessWidget {
2526

2627
return BlocBuilder<SignInBloc, SignInState>(
2728
builder: (context, state) {
29+
final bottomPadding = UniversalPlatform.isDesktop ? 20.0 : 24.0;
2830
return Scaffold(
2931
appBar: _buildAppBar(),
3032
body: Center(
@@ -40,7 +42,10 @@ class DesktopSignInScreen extends StatelessWidget {
4042
VSpace(theme.spacing.xxl),
4143

4244
// continue with email and password
43-
const ContinueWithEmailAndPassword(),
45+
isLocalAuthEnabled
46+
? const SignInAnonymousButtonV3()
47+
: const ContinueWithEmailAndPassword(),
48+
4449
VSpace(theme.spacing.xxl),
4550

4651
// third-party sign in.
@@ -65,7 +70,7 @@ class DesktopSignInScreen extends StatelessWidget {
6570
SignInAnonymousButtonV2(),
6671
],
6772
),
68-
const VSpace(16),
73+
VSpace(bottomPadding),
6974
],
7075
),
7176
),

frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class MobileSignInScreen extends StatelessWidget {
4040
? const SignInAnonymousButtonV3()
4141
: const ContinueWithEmailAndPassword(),
4242
const VSpace(spacing),
43-
if (isAuthEnabled) _buildThirdPartySignInButtons(colorScheme),
43+
if (isAuthEnabled) _buildThirdPartySignInButtons(context),
4444
const VSpace(spacing * 1.5),
4545
const SignInAgreement(),
4646
const VSpace(spacing),
@@ -75,7 +75,8 @@ class MobileSignInScreen extends StatelessWidget {
7575
);
7676
}
7777

78-
Widget _buildThirdPartySignInButtons(ColorScheme colorScheme) {
78+
Widget _buildThirdPartySignInButtons(BuildContext context) {
79+
final theme = AppFlowyTheme.of(context);
7980
return Column(
8081
children: [
8182
Row(
@@ -84,10 +85,12 @@ class MobileSignInScreen extends StatelessWidget {
8485
const Expanded(child: Divider()),
8586
Padding(
8687
padding: const EdgeInsets.symmetric(horizontal: 8),
87-
child: FlowyText(
88+
child: Text(
8889
LocaleKeys.signIn_or.tr(),
89-
fontSize: 12,
90-
color: colorScheme.onSecondary,
90+
style: TextStyle(
91+
fontSize: 16,
92+
color: theme.textColorScheme.secondary,
93+
),
9194
),
9295
),
9396
const Expanded(child: Divider()),

frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/sign_in_screen.dart

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@ import 'package:appflowy/startup/startup.dart';
22
import 'package:appflowy/user/application/sign_in_bloc.dart';
33
import 'package:appflowy/user/presentation/router.dart';
44
import 'package:appflowy/user/presentation/screens/sign_in_screen/desktop_sign_in_screen.dart';
5-
import 'package:appflowy/user/presentation/screens/sign_in_screen/mobile_loading_screen.dart';
65
import 'package:appflowy/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart';
6+
import 'package:appflowy_backend/log.dart';
77
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
88
import 'package:flutter/material.dart';
99
import 'package:flutter_bloc/flutter_bloc.dart';
1010
import 'package:universal_platform/universal_platform.dart';
1111

12-
import '../../helpers/helpers.dart';
13-
1412
class SignInScreen extends StatelessWidget {
1513
const SignInScreen({super.key});
1614

@@ -23,13 +21,9 @@ class SignInScreen extends StatelessWidget {
2321
child: BlocConsumer<SignInBloc, SignInState>(
2422
listener: _showSignInError,
2523
builder: (context, state) {
26-
final isLoading = context.read<SignInBloc>().state.isSubmitting;
27-
if (UniversalPlatform.isMobile) {
28-
return isLoading
29-
? const MobileLoadingScreen()
30-
: const MobileSignInScreen();
31-
}
32-
return const DesktopSignInScreen();
24+
return UniversalPlatform.isDesktop
25+
? const DesktopSignInScreen()
26+
: const MobileSignInScreen();
3327
},
3428
),
3529
);
@@ -47,7 +41,7 @@ class SignInScreen extends StatelessWidget {
4741
}
4842
},
4943
(error) {
50-
handleOpenWorkspaceError(context, error);
44+
Log.error('Sign in error: $error');
5145
},
5246
);
5347
}

frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/anonymous_sign_in_button.dart

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import 'package:appflowy/generated/locale_keys.g.dart';
22
import 'package:appflowy/startup/startup.dart';
33
import 'package:appflowy/user/application/anon_user_bloc.dart';
44
import 'package:appflowy/user/application/sign_in_bloc.dart';
5+
import 'package:appflowy_ui/appflowy_ui.dart';
56
import 'package:easy_localization/easy_localization.dart';
6-
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
77
import 'package:flutter/material.dart';
88
import 'package:flutter_bloc/flutter_bloc.dart';
99

@@ -41,17 +41,11 @@ class SignInAnonymousButtonV3 extends StatelessWidget {
4141
final user = bloc.state.anonUsers.first;
4242
bloc.add(AnonUserEvent.openAnonUser(user));
4343
};
44-
return ElevatedButton(
45-
style: ElevatedButton.styleFrom(
46-
minimumSize: const Size(double.infinity, 32),
47-
maximumSize: const Size(double.infinity, 38),
48-
),
49-
onPressed: onTap,
50-
child: FlowyText(
51-
text,
52-
fontSize: 14,
53-
color: Theme.of(context).colorScheme.onPrimary,
54-
),
44+
return AFFilledTextButton.primary(
45+
text: text,
46+
size: AFButtonSize.l,
47+
alignment: Alignment.center,
48+
onTap: onTap,
5549
);
5650
},
5751
),

frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_email_and_password.dart

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class _ContinueWithEmailAndPasswordState
2323
final focusNode = FocusNode();
2424
final emailKey = GlobalKey<AFTextFieldState>();
2525

26+
bool _hasPushedContinueWithMagicLinkOrPasscodePage = false;
27+
2628
@override
2729
void dispose() {
2830
controller.dispose();
@@ -47,10 +49,12 @@ class _ContinueWithEmailAndPasswordState
4749
),
4850
);
4951
} else if (successOrFail == null && !state.isSubmitting) {
50-
_pushContinueWithMagicLinkOrPasscodePage(
51-
context,
52-
controller.text,
53-
);
52+
emailKey.currentState?.clearError();
53+
54+
// _pushContinueWithMagicLinkOrPasscodePage(
55+
// context,
56+
// controller.text,
57+
// );
5458
}
5559
},
5660
child: Column(
@@ -95,12 +99,21 @@ class _ContinueWithEmailAndPasswordState
9599
context
96100
.read<SignInBloc>()
97101
.add(SignInEvent.signInWithMagicLink(email: email));
102+
103+
_pushContinueWithMagicLinkOrPasscodePage(
104+
context,
105+
email,
106+
);
98107
}
99108

100109
void _pushContinueWithMagicLinkOrPasscodePage(
101110
BuildContext context,
102111
String email,
103112
) {
113+
if (_hasPushedContinueWithMagicLinkOrPasscodePage) {
114+
return;
115+
}
116+
104117
final signInBloc = context.read<SignInBloc>();
105118

106119
// push the a continue with magic link or passcode screen
@@ -111,7 +124,13 @@ class _ContinueWithEmailAndPasswordState
111124
value: signInBloc,
112125
child: ContinueWithMagicLinkOrPasscodePage(
113126
email: email,
114-
backToLogin: () => Navigator.pop(context),
127+
backToLogin: () {
128+
Navigator.pop(context);
129+
130+
emailKey.currentState?.clearError();
131+
132+
_hasPushedContinueWithMagicLinkOrPasscodePage = false;
133+
},
115134
onEnterPasscode: (passcode) => signInBloc.add(
116135
SignInEvent.signInWithPasscode(
117136
email: email,
@@ -122,6 +141,8 @@ class _ContinueWithEmailAndPasswordState
122141
),
123142
),
124143
);
144+
145+
_hasPushedContinueWithMagicLinkOrPasscodePage = true;
125146
}
126147

127148
// void _pushContinueWithPasswordPage(

frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/sign_in_anonymous_button.dart

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'package:appflowy/user/application/anon_user_bloc.dart';
66
import 'package:appflowy/user/application/sign_in_bloc.dart';
77
import 'package:appflowy_ui/appflowy_ui.dart';
88
import 'package:easy_localization/easy_localization.dart';
9-
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
109
import 'package:flutter/material.dart';
1110
import 'package:flutter_bloc/flutter_bloc.dart';
1211

@@ -78,13 +77,16 @@ class ChangeCloudModeButton extends StatelessWidget {
7877

7978
@override
8079
Widget build(BuildContext context) {
81-
return FlowyButton(
82-
useIntrinsicWidth: true,
83-
text: FlowyText(
84-
'Cloud',
85-
decoration: TextDecoration.underline,
86-
color: Colors.grey,
87-
fontSize: 12,
80+
final theme = AppFlowyTheme.of(context);
81+
return AFGhostIconTextButton(
82+
text: 'Cloud',
83+
textColor: (context, isHovering, disabled) {
84+
return theme.textColorScheme.secondary;
85+
},
86+
size: AFButtonSize.s,
87+
padding: EdgeInsets.symmetric(
88+
horizontal: theme.spacing.m,
89+
vertical: theme.spacing.xs,
8890
),
8991
onTap: () async {
9092
await useAppFlowyBetaCloudWithURL(
@@ -93,6 +95,13 @@ class ChangeCloudModeButton extends StatelessWidget {
9395
);
9496
await runAppFlowy();
9597
},
98+
iconBuilder: (context, isHovering, disabled) {
99+
return FlowySvg(
100+
FlowySvgs.settings_s,
101+
size: Size.square(20),
102+
color: theme.textColorScheme.secondary,
103+
);
104+
},
96105
);
97106
}
98107
}

frontend/appflowy_flutter/macos/Runner/Configs/AppInfo.xcconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ PRODUCT_NAME = AppFlowy
1111
PRODUCT_BUNDLE_IDENTIFIER = io.appflowy.appflowy
1212

1313
// The copyright displayed in application information
14-
PRODUCT_COPYRIGHT = Copyright © 2024 AppFlowy.IO. All rights reserved.
14+
PRODUCT_COPYRIGHT = Copyright © 2025 AppFlowy.IO. All rights reserved.

frontend/appflowy_flutter/packages/appflowy_ui/lib/src/theme/data/builder.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class AppFlowyThemeBuilder {
191191
quaternary: colorScheme.neutral.neutral1000,
192192
quaternaryHover: colorScheme.neutral.neutral900,
193193
transparent: colorScheme.neutral.alphaWhite0,
194-
primaryAlpha5: colorScheme.neutral.alphaGrey100005,
194+
primaryAlpha5: colorScheme.neutral.alphaGrey10005,
195195
primaryAlpha5Hover: colorScheme.neutral.alphaGrey10010,
196196
primaryAlpha80: colorScheme.neutral.alphaGrey100080,
197197
primaryAlpha80Hover: colorScheme.neutral.alphaGrey100070,

0 commit comments

Comments
 (0)