feat: add loading dialog

This commit is contained in:
LucasXu0 2025-04-10 11:03:49 +08:00
parent 0344afddf4
commit 41c2a4c9b0
8 changed files with 105 additions and 61 deletions

View file

@ -276,10 +276,11 @@ class _InviteMemberPageState extends State<_InviteMemberPage> {
void _inviteMember(BuildContext context) { void _inviteMember(BuildContext context) {
final email = emailController.text; final email = emailController.text;
if (!isEmail(email)) { if (!isEmail(email)) {
return showToastNotification( showToastNotification(
type: ToastificationType.error, type: ToastificationType.error,
message: LocaleKeys.settings_appearance_members_emailInvalidError.tr(), message: LocaleKeys.settings_appearance_members_emailInvalidError.tr(),
); );
return;
} }
context context
.read<WorkspaceMemberBloc>() .read<WorkspaceMemberBloc>()

View file

@ -104,9 +104,10 @@ Future<void> downloadMediaFile(
await afLaunchUrlString(file.url); await afLaunchUrlString(file.url);
} else { } else {
if (userProfile == null) { if (userProfile == null) {
return showToastNotification( showToastNotification(
message: LocaleKeys.grid_media_downloadFailedToken.tr(), message: LocaleKeys.grid_media_downloadFailedToken.tr(),
); );
return;
} }
final uri = Uri.parse(file.url); final uri = Uri.parse(file.url);

View file

@ -2,8 +2,6 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/user/application/sign_in_bloc.dart'; import 'package:appflowy/user/application/sign_in_bloc.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_email.dart'; import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_email.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_magic_link_or_passcode_page.dart'; import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_magic_link_or_passcode_page.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_password.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_password_page.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_ui/appflowy_ui.dart'; import 'package:appflowy_ui/appflowy_ui.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -59,13 +57,14 @@ class _ContinueWithEmailAndPasswordState
controller.text, controller.text,
), ),
), ),
VSpace(theme.spacing.l), // Hide password sign in until we implement the reset password / forgot password
ContinueWithPassword( // VSpace(theme.spacing.l),
onTap: () => _pushContinueWithPasswordPage( // ContinueWithPassword(
context, // onTap: () => _pushContinueWithPasswordPage(
controller.text, // context,
), // controller.text,
), // ),
// ),
], ],
); );
} }
@ -75,10 +74,11 @@ class _ContinueWithEmailAndPasswordState
String email, String email,
) { ) {
if (!isEmail(email)) { if (!isEmail(email)) {
return showToastNotification( showToastNotification(
message: LocaleKeys.signIn_invalidEmail.tr(), message: LocaleKeys.signIn_invalidEmail.tr(),
type: ToastificationType.error, type: ToastificationType.error,
); );
return;
} }
final signInBloc = context.read<SignInBloc>(); final signInBloc = context.read<SignInBloc>();
@ -89,13 +89,16 @@ class _ContinueWithEmailAndPasswordState
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => ContinueWithMagicLinkOrPasscodePage( builder: (context) => BlocProvider.value(
email: email, value: signInBloc,
backToLogin: () => Navigator.pop(context), child: ContinueWithMagicLinkOrPasscodePage(
onEnterPasscode: (passcode) => signInBloc.add( email: email,
SignInEvent.signInWithPasscode( backToLogin: () => Navigator.pop(context),
email: email, onEnterPasscode: (passcode) => signInBloc.add(
passcode: passcode, SignInEvent.signInWithPasscode(
email: email,
passcode: passcode,
),
), ),
), ),
), ),
@ -103,28 +106,28 @@ class _ContinueWithEmailAndPasswordState
); );
} }
void _pushContinueWithPasswordPage( // void _pushContinueWithPasswordPage(
BuildContext context, // BuildContext context,
String email, // String email,
) { // ) {
final signInBloc = context.read<SignInBloc>(); // final signInBloc = context.read<SignInBloc>();
Navigator.push( // Navigator.push(
context, // context,
MaterialPageRoute( // MaterialPageRoute(
builder: (context) => ContinueWithPasswordPage( // builder: (context) => ContinueWithPasswordPage(
email: email, // email: email,
backToLogin: () => Navigator.pop(context), // backToLogin: () => Navigator.pop(context),
onEnterPassword: (password) => signInBloc.add( // onEnterPassword: (password) => signInBloc.add(
SignInEvent.signInWithEmailAndPassword( // SignInEvent.signInWithEmailAndPassword(
email: email, // email: email,
password: password, // password: password,
), // ),
), // ),
onForgotPassword: () { // onForgotPassword: () {
// todo: implement forgot password // // todo: implement forgot password
}, // },
), // ),
), // ),
); // );
} // }
} }

View file

@ -1,7 +1,10 @@
import 'package:appflowy/user/application/sign_in_bloc.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/logo/logo.dart'; import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/logo/logo.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_ui/appflowy_ui.dart'; import 'package:appflowy_ui/appflowy_ui.dart';
import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ContinueWithMagicLinkOrPasscodePage extends StatefulWidget { class ContinueWithMagicLinkOrPasscodePage extends StatefulWidget {
const ContinueWithMagicLinkOrPasscodePage({ const ContinueWithMagicLinkOrPasscodePage({
@ -26,6 +29,8 @@ class _ContinueWithMagicLinkOrPasscodePageState
bool isEnteringPasscode = false; bool isEnteringPasscode = false;
ToastificationItem? toastificationItem;
@override @override
void dispose() { void dispose() {
passcodeController.dispose(); passcodeController.dispose();
@ -35,22 +40,31 @@ class _ContinueWithMagicLinkOrPasscodePageState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return BlocListener<SignInBloc, SignInState>(
body: Center( listener: (context, state) {
child: SizedBox( if (state.isSubmitting) {
width: 320, _showLoadingDialog();
child: Column( } else {
mainAxisAlignment: MainAxisAlignment.center, _dismissLoadingDialog();
children: [ }
// Logo, title and description },
..._buildLogoTitleAndDescription(), child: Scaffold(
body: Center(
child: SizedBox(
width: 320,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Logo, title and description
..._buildLogoTitleAndDescription(),
// Enter code manually // Enter code manually
..._buildEnterCodeManually(), ..._buildEnterCodeManually(),
// Back to login // Back to login
..._buildBackToLogin(), ..._buildBackToLogin(),
], ],
),
), ),
), ),
), ),
@ -91,7 +105,7 @@ class _ContinueWithMagicLinkOrPasscodePageState
// continue to login // continue to login
AFFilledTextButton.primary( AFFilledTextButton.primary(
text: 'Continue to sign up', text: 'Continue to sign in',
onTap: () => widget.onEnterPasscode(passcodeController.text), onTap: () => widget.onEnterPasscode(passcodeController.text),
size: AFButtonSize.l, size: AFButtonSize.l,
alignment: Alignment.center, alignment: Alignment.center,
@ -109,6 +123,9 @@ class _ContinueWithMagicLinkOrPasscodePageState
onTap: widget.backToLogin, onTap: widget.backToLogin,
textColor: (context, isHovering, disabled) { textColor: (context, isHovering, disabled) {
final theme = AppFlowyTheme.of(context); final theme = AppFlowyTheme.of(context);
if (isHovering) {
return theme.fillColorScheme.themeThickHover;
}
return theme.textColorScheme.theme; return theme.textColorScheme.theme;
}, },
), ),
@ -150,4 +167,19 @@ class _ContinueWithMagicLinkOrPasscodePageState
spacing, spacing,
]; ];
} }
void _showLoadingDialog() {
_dismissLoadingDialog();
toastificationItem = showToastNotification(
message: 'Signing in...',
);
}
void _dismissLoadingDialog() {
final toastificationItem = this.toastificationItem;
if (toastificationItem != null) {
toastification.dismiss(toastificationItem);
}
}
} }

View file

@ -64,10 +64,11 @@ class _SignInWithMagicLinkButtonsState
void _sendMagicLink(BuildContext context, String email) { void _sendMagicLink(BuildContext context, String email) {
if (!isEmail(email)) { if (!isEmail(email)) {
return showToastNotification( showToastNotification(
message: LocaleKeys.signIn_invalidEmail.tr(), message: LocaleKeys.signIn_invalidEmail.tr(),
type: ToastificationType.error, type: ToastificationType.error,
); );
return;
} }
context context

View file

@ -104,6 +104,9 @@ class _DesktopThirdPartySignInState extends State<_DesktopThirdPartySignIn> {
AFGhostTextButton( AFGhostTextButton(
text: 'More options', text: 'More options',
textColor: (context, isHovering, disabled) { textColor: (context, isHovering, disabled) {
if (isHovering) {
return theme.fillColorScheme.themeThickHover;
}
return theme.textColorScheme.theme; return theme.textColorScheme.theme;
}, },
onTap: () { onTap: () {

View file

@ -188,6 +188,9 @@ class SidebarPlanBloc extends Bloc<SidebarPlanEvent, SidebarPlanState> {
UserEventGetWorkspaceUsage(payload).send().then((result) { UserEventGetWorkspaceUsage(payload).send().then((result) {
result.onSuccess( result.onSuccess(
(usage) { (usage) {
if (isClosed) {
return;
}
add(SidebarPlanEvent.updateWorkspaceUsage(usage)); add(SidebarPlanEvent.updateWorkspaceUsage(usage));
}, },
); );

View file

@ -362,7 +362,7 @@ class OkCancelButton extends StatelessWidget {
} }
} }
void showToastNotification({ ToastificationItem showToastNotification({
String? message, String? message,
TextSpan? richMessage, TextSpan? richMessage,
String? description, String? description,
@ -374,7 +374,7 @@ void showToastNotification({
(message == null) != (richMessage == null), (message == null) != (richMessage == null),
"Exactly one of message or richMessage must be non-null.", "Exactly one of message or richMessage must be non-null.",
); );
toastification.showCustom( return toastification.showCustom(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
autoCloseDuration: const Duration(milliseconds: 3000), autoCloseDuration: const Duration(milliseconds: 3000),
callbacks: callbacks ?? const ToastificationCallbacks(), callbacks: callbacks ?? const ToastificationCallbacks(),