mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
refactor: login session (#3149)
This commit is contained in:
parent
9330df4ce1
commit
a5eaf15548
@ -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,
|
||||
);
|
||||
}
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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));
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
),
|
||||
|
@ -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
|
||||
|
@ -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": {
|
||||
|
@ -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(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user