Bläddra i källkod

refactor: login session (#3149)

Nathan.fooo 1 år sedan
förälder
incheckning
a5eaf15548

+ 64 - 0
frontend/appflowy_flutter/lib/user/application/historical_user_bloc.dart

@@ -0,0 +1,64 @@
+import 'package:appflowy/user/application/user_service.dart';
+import 'package:appflowy_backend/log.dart';
+import 'package:appflowy_backend/protobuf/flowy-user/auth.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'historical_user_bloc.freezed.dart';
+
+class HistoricalUserBloc
+    extends Bloc<HistoricalUserEvent, HistoricalUserState> {
+  HistoricalUserBloc() : super(HistoricalUserState.initial()) {
+    on<HistoricalUserEvent>((event, emit) async {
+      await event.when(
+        initial: () async {
+          await _loadHistoricalUsers();
+        },
+        didLoadHistoricalUsers: (List<HistoricalUserPB> historicalUsers) {
+          emit(state.copyWith(historicalUsers: historicalUsers));
+        },
+        openHistoricalUser: (HistoricalUserPB historicalUser) async {
+          await UserBackendService.openHistoricalUser(historicalUser);
+          emit(state.copyWith(openedHistoricalUser: historicalUser));
+        },
+      );
+    });
+  }
+
+  Future<void> _loadHistoricalUsers() async {
+    final result = await UserBackendService.loadHistoricalUsers();
+    result.fold(
+      (historicalUsers) {
+        historicalUsers
+            .retainWhere((element) => element.authType == AuthTypePB.Local);
+        add(HistoricalUserEvent.didLoadHistoricalUsers(historicalUsers));
+      },
+      (error) => Log.error(error),
+    );
+  }
+}
+
+@freezed
+class HistoricalUserEvent with _$HistoricalUserEvent {
+  const factory HistoricalUserEvent.initial() = _Initial;
+  const factory HistoricalUserEvent.didLoadHistoricalUsers(
+    List<HistoricalUserPB> historicalUsers,
+  ) = _DidLoadHistoricalUsers;
+  const factory HistoricalUserEvent.openHistoricalUser(
+    HistoricalUserPB historicalUser,
+  ) = _OpenHistoricalUser;
+}
+
+@freezed
+class HistoricalUserState with _$HistoricalUserState {
+  const factory HistoricalUserState({
+    required List<HistoricalUserPB> historicalUsers,
+    required HistoricalUserPB? openedHistoricalUser,
+  }) = _HistoricalUserState;
+
+  factory HistoricalUserState.initial() => const HistoricalUserState(
+        historicalUsers: [],
+        openedHistoricalUser: null,
+      );
+}

+ 2 - 2
frontend/appflowy_flutter/lib/user/application/user_service.dart

@@ -70,7 +70,7 @@ class UserBackendService {
     return UserEventInitUser().send();
   }
 
-  Future<Either<List<HistoricalUserPB>, FlowyError>>
+  static Future<Either<List<HistoricalUserPB>, FlowyError>>
       loadHistoricalUsers() async {
     return UserEventGetHistoricalUsers().send().then(
       (result) {
@@ -82,7 +82,7 @@ class UserBackendService {
     );
   }
 
-  Future<Either<Unit, FlowyError>> openHistoricalUser(
+  static Future<Either<Unit, FlowyError>> openHistoricalUser(
     HistoricalUserPB user,
   ) async {
     return UserEventOpenHistoricalUser(user).send();

+ 100 - 0
frontend/appflowy_flutter/lib/user/presentation/historical_user.dart

@@ -0,0 +1,100 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/user/application/historical_user_bloc.dart';
+import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/image.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class HistoricalUserList extends StatelessWidget {
+  final VoidCallback didOpenUser;
+  const HistoricalUserList({required this.didOpenUser, super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocProvider(
+      create: (context) => HistoricalUserBloc()
+        ..add(
+          const HistoricalUserEvent.initial(),
+        ),
+      child: BlocBuilder<HistoricalUserBloc, HistoricalUserState>(
+        builder: (context, state) {
+          if (state.historicalUsers.isEmpty) {
+            return const SizedBox.shrink();
+          } else {
+            return Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                Opacity(
+                  opacity: 0.6,
+                  child: FlowyText.regular(
+                    LocaleKeys.settings_menu_historicalUserListTooltip.tr(),
+                    fontSize: 13,
+                    maxLines: null,
+                  ),
+                ),
+                const VSpace(6),
+                Expanded(
+                  child: ListView.builder(
+                    itemBuilder: (context, index) {
+                      final user = state.historicalUsers[index];
+                      return HistoricalUserItem(
+                        key: ValueKey(user.userId),
+                        user: user,
+                        isSelected: false,
+                        didOpenUser: didOpenUser,
+                      );
+                    },
+                    itemCount: state.historicalUsers.length,
+                  ),
+                )
+              ],
+            );
+          }
+        },
+      ),
+    );
+  }
+}
+
+class HistoricalUserItem extends StatelessWidget {
+  final VoidCallback didOpenUser;
+  final bool isSelected;
+  final HistoricalUserPB user;
+  const HistoricalUserItem({
+    required this.user,
+    required this.isSelected,
+    required this.didOpenUser,
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    final icon = isSelected ? const FlowySvg(name: "grid/checkmark") : null;
+    final isDisabled = isSelected || user.authType != AuthTypePB.Local;
+    final outputFormat = DateFormat('MM/dd/yyyy hh:mm a');
+    final date =
+        DateTime.fromMillisecondsSinceEpoch(user.lastTime.toInt() * 1000);
+    final lastTime = outputFormat.format(date);
+    final desc = "${user.userName}\t ${user.authType}\t$lastTime";
+    final child = SizedBox(
+      height: 30,
+      child: FlowyButton(
+        disable: isDisabled,
+        text: FlowyText.medium(
+          desc,
+          fontSize: 12,
+        ),
+        rightIcon: icon,
+        onTap: () {
+          context
+              .read<HistoricalUserBloc>()
+              .add(HistoricalUserEvent.openHistoricalUser(user));
+          didOpenUser();
+        },
+      ),
+    );
+    return child;
+  }
+}

+ 57 - 9
frontend/appflowy_flutter/lib/user/presentation/sign_in_screen.dart

@@ -1,7 +1,9 @@
 import 'package:appflowy/core/config/kv.dart';
 import 'package:appflowy/core/config/kv_keys.dart';
 import 'package:appflowy/core/frameless_window.dart';
+import 'package:appflowy/startup/entry_point.dart';
 import 'package:appflowy/startup/startup.dart';
+import 'package:appflowy/user/application/historical_user_bloc.dart';
 import 'package:appflowy/user/application/sign_in_bloc.dart';
 import 'package:appflowy/user/presentation/router.dart';
 import 'package:appflowy/user/presentation/widgets/background.dart';
@@ -118,7 +120,18 @@ class SignInForm extends StatelessWidget {
               : [
                   const VSpace(indicatorMinHeight * 2.0)
                 ], // add the same space when there's no loading status.
-          const VSpace(20)
+          // ConstrainedBox(
+          //   constraints: const BoxConstraints(maxHeight: 140),
+          //   child: HistoricalUserList(
+          //     didOpenUser: () async {
+          //       await FlowyRunner.run(
+          //         FlowyApp(),
+          //         integrationEnv(),
+          //       );
+          //     },
+          //   ),
+          // ),
+          const VSpace(20),
         ],
       ),
     );
@@ -183,14 +196,49 @@ class SignInAsGuestButton extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return RoundedTextButton(
-      title: LocaleKeys.signIn_loginAsGuestButtonText.tr(),
-      height: 48,
-      borderRadius: Corners.s6Border,
-      onPressed: () {
-        getIt<KeyValueStorage>().set(KVKeys.loginType, 'local');
-        context.read<SignInBloc>().add(const SignInEvent.signedInAsGuest());
-      },
+    return BlocProvider(
+      create: (context) => HistoricalUserBloc()
+        ..add(
+          const HistoricalUserEvent.initial(),
+        ),
+      child: BlocListener<HistoricalUserBloc, HistoricalUserState>(
+        listenWhen: (previous, current) =>
+            previous.openedHistoricalUser != current.openedHistoricalUser,
+        listener: (context, state) async {
+          await FlowyRunner.run(
+            FlowyApp(),
+            integrationEnv(),
+          );
+        },
+        child: BlocBuilder<HistoricalUserBloc, HistoricalUserState>(
+          builder: (context, state) {
+            if (state.historicalUsers.isEmpty) {
+              return RoundedTextButton(
+                title: LocaleKeys.signIn_loginAsGuestButtonText.tr(),
+                height: 48,
+                borderRadius: Corners.s6Border,
+                onPressed: () {
+                  getIt<KeyValueStorage>().set(KVKeys.loginType, 'local');
+                  context
+                      .read<SignInBloc>()
+                      .add(const SignInEvent.signedInAsGuest());
+                },
+              );
+            } else {
+              return RoundedTextButton(
+                title: LocaleKeys.signIn_continueAnonymousUser.tr(),
+                height: 48,
+                borderRadius: Corners.s6Border,
+                onPressed: () {
+                  final bloc = context.read<HistoricalUserBloc>();
+                  final user = bloc.state.historicalUsers.first;
+                  bloc.add(HistoricalUserEvent.openHistoricalUser(user));
+                },
+              );
+            }
+          },
+        ),
+      ),
     );
   }
 }

+ 2 - 2
frontend/appflowy_flutter/lib/workspace/application/user/settings_user_bloc.dart

@@ -56,7 +56,7 @@ class SettingsUserViewBloc extends Bloc<SettingsUserEvent, SettingsUserState> {
           emit(state.copyWith(historicalUsers: historicalUsers));
         },
         openHistoricalUser: (HistoricalUserPB historicalUser) async {
-          await _userService.openHistoricalUser(historicalUser);
+          await UserBackendService.openHistoricalUser(historicalUser);
         },
       );
     });
@@ -74,7 +74,7 @@ class SettingsUserViewBloc extends Bloc<SettingsUserEvent, SettingsUserState> {
   }
 
   Future<void> _loadHistoricalUsers() async {
-    final result = await _userService.loadHistoricalUsers();
+    final result = await UserBackendService.loadHistoricalUsers();
     result.fold(
       (historicalUsers) {
         add(SettingsUserEvent.didLoadHistoricalUsers(historicalUsers));

+ 0 - 110
frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/historical_user.dart

@@ -1,110 +0,0 @@
-import 'package:appflowy/generated/locale_keys.g.dart';
-import 'package:appflowy/workspace/application/user/settings_user_bloc.dart';
-import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flowy_infra/image.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-class HistoricalUserList extends StatelessWidget {
-  final VoidCallback didOpenUser;
-  const HistoricalUserList({required this.didOpenUser, super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return BlocBuilder<SettingsUserViewBloc, SettingsUserState>(
-      builder: (context, state) {
-        return ConstrainedBox(
-          constraints: const BoxConstraints(maxHeight: 200),
-          child: Column(
-            crossAxisAlignment: CrossAxisAlignment.start,
-            children: [
-              Row(
-                children: [
-                  FlowyText.medium(
-                    LocaleKeys.settings_menu_historicalUserList.tr(),
-                    fontSize: 13,
-                  ),
-                  const Spacer(),
-                  Tooltip(
-                    message:
-                        LocaleKeys.settings_menu_historicalUserListTooltip.tr(),
-                    child: const Icon(
-                      Icons.question_mark_rounded,
-                      size: 16,
-                    ),
-                  ),
-                ],
-              ),
-              Expanded(
-                child: ListView.builder(
-                  itemBuilder: (context, index) {
-                    final user = state.historicalUsers[index];
-                    return HistoricalUserItem(
-                      key: ValueKey(user.userId),
-                      user: user,
-                      isSelected: state.userProfile.id == user.userId,
-                      didOpenUser: didOpenUser,
-                    );
-                  },
-                  itemCount: state.historicalUsers.length,
-                ),
-              )
-            ],
-          ),
-        );
-      },
-    );
-  }
-}
-
-class HistoricalUserItem extends StatelessWidget {
-  final VoidCallback didOpenUser;
-  final bool isSelected;
-  final HistoricalUserPB user;
-  const HistoricalUserItem({
-    required this.user,
-    required this.isSelected,
-    required this.didOpenUser,
-    super.key,
-  });
-
-  @override
-  Widget build(BuildContext context) {
-    final icon = isSelected ? const FlowySvg(name: "grid/checkmark") : null;
-    final isDisabled = isSelected || user.authType != AuthTypePB.Local;
-    final outputFormat = DateFormat('MM/dd/yyyy');
-    final date =
-        DateTime.fromMillisecondsSinceEpoch(user.lastTime.toInt() * 1000);
-    final lastTime = outputFormat.format(date);
-    final desc = "${user.userName}  ${user.authType}  $lastTime";
-    final child = SizedBox(
-      height: 30,
-      child: FlowyButton(
-        disable: isDisabled,
-        text: FlowyText.medium(desc),
-        rightIcon: icon,
-        onTap: () {
-          if (user.userId ==
-              context.read<SettingsUserViewBloc>().userProfile.id) {
-            return;
-          }
-          context
-              .read<SettingsUserViewBloc>()
-              .add(SettingsUserEvent.openHistoricalUser(user));
-          didOpenUser();
-        },
-      ),
-    );
-
-    if (isSelected) {
-      return child;
-    } else {
-      return Tooltip(
-        message: LocaleKeys.settings_menu_openHistoricalUser.tr(),
-        child: child,
-      );
-    }
-  }
-}

+ 5 - 1
frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_third_party_login.dart

@@ -31,7 +31,11 @@ class SettingThirdPartyLogin extends StatelessWidget {
         builder: (_, __) => Column(
           crossAxisAlignment: CrossAxisAlignment.start,
           children: [
-            FlowyText.medium(LocaleKeys.signIn_signInWith.tr()),
+            FlowyText.medium(
+              LocaleKeys.signIn_signInWith.tr(),
+              fontSize: 16,
+            ),
+            const VSpace(6),
             const ThirdPartySignInButtons(
               mainAxisAlignment: MainAxisAlignment.start,
             ),

+ 1 - 12
frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_user_view.dart

@@ -16,7 +16,6 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import 'historical_user.dart';
 import 'setting_third_party_login.dart';
 
 const defaultUserAvatar = '1F600';
@@ -56,7 +55,7 @@ class SettingsUserView extends StatelessWidget {
               const VSpace(20),
               _renderCurrentOpenaiKey(context),
               const VSpace(20),
-              _renderHistoricalUser(context),
+              // _renderHistoricalUser(context),
               _renderLoginOrLogoutButton(context, state),
               const VSpace(20),
             ],
@@ -129,16 +128,6 @@ class SettingsUserView extends StatelessWidget {
       ),
     );
   }
-
-  Widget _renderHistoricalUser(BuildContext context) {
-    return BlocBuilder<SettingsUserViewBloc, SettingsUserState>(
-      builder: (context, state) {
-        return HistoricalUserList(
-          didOpenUser: didOpenUser,
-        );
-      },
-    );
-  }
 }
 
 @visibleForTesting

+ 4 - 3
frontend/resources/translations/en.json

@@ -33,6 +33,7 @@
     "loginTitle": "Login to @:appName",
     "loginButtonText": "Login",
     "loginAsGuestButtonText": "Get Started",
+    "continueAnonymousUser": "Continue in an anonymous session",
     "buttonText": "Sign In",
     "forgotPassword": "Forgot Password?",
     "emailHint": "Email",
@@ -227,9 +228,9 @@
       "logoutPrompt": "Are you sure to logout?",
       "syncSetting": "Sync Setting",
       "enableSync": "Enable sync",
-      "historicalUserList": "User history",
-      "historicalUserListTooltip": "This list shows your login history. You can click to login if it's a local user",
-      "openHistoricalUser": "Click to open user"
+      "historicalUserList": "User login history",
+      "historicalUserListTooltip": "This list displays your anonymous accounts. You can click on an account to view its details. Anonymous accounts are created by clicking the 'Get Started' button",
+      "openHistoricalUser": "Click to open the anonymous account"
     },
     "appearance": {
       "fontFamily": {

+ 1 - 0
frontend/rust-lib/flowy-user/src/services/user_session.rs

@@ -616,6 +616,7 @@ impl UserSession {
       user_id: uid,
       user_workspace,
     };
+    self.cloud_services.set_auth_type(AuthType::Local);
     self.set_current_session(Some(session))?;
     Ok(())
   }