mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #590 from ENsu/add_workspace_settings
Add workspace settings
This commit is contained in:
commit
b0f26f9450
@ -141,6 +141,7 @@
|
||||
"menu": {
|
||||
"appearance": "Appearance",
|
||||
"language": "Language",
|
||||
"user": "User",
|
||||
"open": "Open Settings"
|
||||
},
|
||||
"appearance": {
|
||||
|
@ -5,10 +5,12 @@ import 'package:app_flowy/workspace/application/app/prelude.dart';
|
||||
import 'package:app_flowy/workspace/application/doc/prelude.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
||||
import 'package:app_flowy/workspace/application/trash/prelude.dart';
|
||||
import 'package:app_flowy/workspace/application/user/prelude.dart';
|
||||
import 'package:app_flowy/workspace/application/workspace/prelude.dart';
|
||||
import 'package:app_flowy/workspace/application/edit_pannel/edit_pannel_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/view/prelude.dart';
|
||||
import 'package:app_flowy/workspace/application/menu/prelude.dart';
|
||||
import 'package:app_flowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:app_flowy/user/application/prelude.dart';
|
||||
import 'package:app_flowy/user/presentation/router.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
@ -101,6 +103,16 @@ void _resolveFolderDeps(GetIt getIt) {
|
||||
(user, _) => MenuUserBloc(user),
|
||||
);
|
||||
|
||||
//Settings
|
||||
getIt.registerFactoryParam<SettingsDialogBloc, UserProfilePB, void>(
|
||||
(user, _) => SettingsDialogBloc(user),
|
||||
);
|
||||
|
||||
//User
|
||||
getIt.registerFactoryParam<SettingsUserViewBloc, UserProfilePB, void>(
|
||||
(user, _) => SettingsUserViewBloc(user),
|
||||
);
|
||||
|
||||
// AppPB
|
||||
getIt.registerFactoryParam<AppBloc, AppPB, void>(
|
||||
(app, _) => AppBloc(
|
||||
|
@ -0,0 +1 @@
|
||||
export 'settings_dialog_bloc.dart';
|
@ -0,0 +1,67 @@
|
||||
import 'package:app_flowy/user/application/user_listener.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
part 'settings_dialog_bloc.freezed.dart';
|
||||
|
||||
class SettingsDialogBloc extends Bloc<SettingsDialogEvent, SettingsDialogState> {
|
||||
final UserListener _userListener;
|
||||
final UserProfilePB userProfile;
|
||||
|
||||
SettingsDialogBloc(this.userProfile)
|
||||
: _userListener = UserListener(userProfile: userProfile),
|
||||
super(SettingsDialogState.initial(userProfile)) {
|
||||
on<SettingsDialogEvent>((event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
_userListener.start(onProfileUpdated: _profileUpdated);
|
||||
},
|
||||
didReceiveUserProfile: (UserProfilePB newUserProfile) {
|
||||
emit(state.copyWith(userProfile: newUserProfile));
|
||||
},
|
||||
setViewIndex: (int viewIndex) {
|
||||
emit(state.copyWith(viewIndex: viewIndex));
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _userListener.stop();
|
||||
super.close();
|
||||
}
|
||||
|
||||
void _profileUpdated(Either<UserProfilePB, FlowyError> userProfileOrFailed) {
|
||||
userProfileOrFailed.fold(
|
||||
(newUserProfile) => add(SettingsDialogEvent.didReceiveUserProfile(newUserProfile)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SettingsDialogEvent with _$SettingsDialogEvent {
|
||||
const factory SettingsDialogEvent.initial() = _Initial;
|
||||
const factory SettingsDialogEvent.didReceiveUserProfile(UserProfilePB newUserProfile) = _DidReceiveUserProfile;
|
||||
const factory SettingsDialogEvent.setViewIndex(int index) = _SetViewIndex;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SettingsDialogState with _$SettingsDialogState {
|
||||
const factory SettingsDialogState({
|
||||
required UserProfilePB userProfile,
|
||||
required Either<Unit, String> successOrFailure,
|
||||
required int viewIndex,
|
||||
}) = _SettingsDialogState;
|
||||
|
||||
factory SettingsDialogState.initial(UserProfilePB userProfile) => SettingsDialogState(
|
||||
userProfile: userProfile,
|
||||
successOrFailure: left(unit),
|
||||
viewIndex: 0,
|
||||
);
|
||||
}
|
@ -0,0 +1 @@
|
||||
export 'settings_user_bloc.dart';
|
@ -0,0 +1,79 @@
|
||||
import 'package:app_flowy/user/application/user_listener.dart';
|
||||
import 'package:app_flowy/user/application/user_service.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
part 'settings_user_bloc.freezed.dart';
|
||||
|
||||
class SettingsUserViewBloc extends Bloc<SettingsUserEvent, SettingsUserState> {
|
||||
final UserService _userService;
|
||||
final UserListener _userListener;
|
||||
final UserProfilePB userProfile;
|
||||
|
||||
SettingsUserViewBloc(this.userProfile)
|
||||
: _userListener = UserListener(userProfile: userProfile),
|
||||
_userService = UserService(userId: userProfile.id),
|
||||
super(SettingsUserState.initial(userProfile)) {
|
||||
on<SettingsUserEvent>((event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
_userListener.start(onProfileUpdated: _profileUpdated);
|
||||
await _initUser();
|
||||
},
|
||||
didReceiveUserProfile: (UserProfilePB newUserProfile) {
|
||||
emit(state.copyWith(userProfile: newUserProfile));
|
||||
},
|
||||
updateUserName: (String name) {
|
||||
_userService.updateUserProfile(name: name).then((result) {
|
||||
result.fold(
|
||||
(l) => null,
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _userListener.stop();
|
||||
super.close();
|
||||
}
|
||||
|
||||
Future<void> _initUser() async {
|
||||
final result = await _userService.initUser();
|
||||
result.fold((l) => null, (error) => Log.error(error));
|
||||
}
|
||||
|
||||
void _profileUpdated(Either<UserProfilePB, FlowyError> userProfileOrFailed) {
|
||||
userProfileOrFailed.fold(
|
||||
(newUserProfile) => add(SettingsUserEvent.didReceiveUserProfile(newUserProfile)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SettingsUserEvent with _$SettingsUserEvent {
|
||||
const factory SettingsUserEvent.initial() = _Initial;
|
||||
const factory SettingsUserEvent.updateUserName(String name) = _UpdateUserName;
|
||||
const factory SettingsUserEvent.didReceiveUserProfile(UserProfilePB newUserProfile) = _DidReceiveUserProfile;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SettingsUserState with _$SettingsUserState {
|
||||
const factory SettingsUserState({
|
||||
required UserProfilePB userProfile,
|
||||
required Either<Unit, String> successOrFailure,
|
||||
}) = _SettingsUserState;
|
||||
|
||||
factory SettingsUserState.initial(UserProfilePB userProfile) => SettingsUserState(
|
||||
userProfile: userProfile,
|
||||
successOrFailure: left(unit),
|
||||
);
|
||||
}
|
@ -67,6 +67,7 @@ class MenuUser extends StatelessWidget {
|
||||
|
||||
Widget _renderSettingsButton(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final userProfile = context.read<MenuUserBloc>().state.userProfile;
|
||||
return Tooltip(
|
||||
message: LocaleKeys.settings_menu_open.tr(),
|
||||
child: IconButton(
|
||||
@ -74,7 +75,7 @@ class MenuUser extends StatelessWidget {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return const SettingsDialog();
|
||||
return SettingsDialog(userProfile);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -1,70 +1,75 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/workspace/application/appearance.dart';
|
||||
import 'package:app_flowy/workspace/presentation/settings/widgets/settings_appearance_view.dart';
|
||||
import 'package:app_flowy/workspace/presentation/settings/widgets/settings_language_view.dart';
|
||||
import 'package:app_flowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:app_flowy/workspace/presentation/settings/widgets/settings_menu.dart';
|
||||
import 'package:app_flowy/workspace/application/settings/settings_dialog_bloc.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class SettingsDialog extends StatefulWidget {
|
||||
const SettingsDialog({Key? key}) : super(key: key);
|
||||
class SettingsDialog extends StatelessWidget {
|
||||
final UserProfilePB user;
|
||||
SettingsDialog(this.user, {Key? key}) : super(key: ValueKey(user.id));
|
||||
|
||||
@override
|
||||
State<SettingsDialog> createState() => _SettingsDialogState();
|
||||
}
|
||||
|
||||
class _SettingsDialogState extends State<SettingsDialog> {
|
||||
int _selectedViewIndex = 0;
|
||||
|
||||
final List<Widget> settingsViews = const [
|
||||
SettingsAppearanceView(),
|
||||
SettingsLanguageView(),
|
||||
];
|
||||
Widget getSettingsView(int index, UserProfilePB user) {
|
||||
final List<Widget> settingsViews = [
|
||||
const SettingsAppearanceView(),
|
||||
const SettingsLanguageView(),
|
||||
SettingsUserView(user),
|
||||
];
|
||||
return settingsViews[index];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider.value(
|
||||
value: Provider.of<AppearanceSettingModel>(context, listen: true),
|
||||
child: AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
title: Text(
|
||||
LocaleKeys.settings_title.tr(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 600,
|
||||
minWidth: 600,
|
||||
maxWidth: 1000,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: SettingsMenu(
|
||||
changeSelectedIndex: (index) {
|
||||
setState(() {
|
||||
_selectedViewIndex = index;
|
||||
});
|
||||
},
|
||||
currentIndex: _selectedViewIndex,
|
||||
),
|
||||
),
|
||||
const VerticalDivider(),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: settingsViews[_selectedViewIndex],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return BlocProvider<SettingsDialogBloc>(
|
||||
create: (context) => getIt<SettingsDialogBloc>(param1: user)..add(const SettingsDialogEvent.initial()),
|
||||
child: BlocBuilder<SettingsDialogBloc, SettingsDialogState>(
|
||||
builder: (context, state) => ChangeNotifierProvider.value(
|
||||
value: Provider.of<AppearanceSettingModel>(context, listen: true),
|
||||
child: AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
title: Text(
|
||||
LocaleKeys.settings_title.tr(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 600,
|
||||
minWidth: 600,
|
||||
maxWidth: 1000,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: SettingsMenu(
|
||||
changeSelectedIndex: (index) {
|
||||
context.read<SettingsDialogBloc>().add(SettingsDialogEvent.setViewIndex(index));
|
||||
},
|
||||
currentIndex: context.read<SettingsDialogBloc>().state.viewIndex,
|
||||
),
|
||||
),
|
||||
const VerticalDivider(),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: getSettingsView(context.read<SettingsDialogBloc>().state.viewIndex,
|
||||
context.read<SettingsDialogBloc>().state.userProfile),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,16 @@ class SettingsMenu extends StatelessWidget {
|
||||
icon: Icons.translate,
|
||||
changeSelectedIndex: changeSelectedIndex,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SettingsMenuElement(
|
||||
index: 2,
|
||||
currentIndex: currentIndex,
|
||||
label: LocaleKeys.settings_menu_user.tr(),
|
||||
icon: Icons.account_box_outlined,
|
||||
changeSelectedIndex: changeSelectedIndex,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:app_flowy/workspace/application/user/settings_user_bloc.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
|
||||
|
||||
class SettingsUserView extends StatelessWidget {
|
||||
final UserProfilePB user;
|
||||
SettingsUserView(this.user, {Key? key}) : super(key: ValueKey(user.id));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<SettingsUserViewBloc>(
|
||||
create: (context) => getIt<SettingsUserViewBloc>(param1: user)..add(const SettingsUserEvent.initial()),
|
||||
child: BlocBuilder<SettingsUserViewBloc, SettingsUserState>(
|
||||
builder: (context, state) => SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [_renderUserNameInput(context)],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderUserNameInput(BuildContext context) {
|
||||
String name = context.read<SettingsUserViewBloc>().state.userProfile.name;
|
||||
return _UserNameInput(name);
|
||||
}
|
||||
}
|
||||
|
||||
class _UserNameInput extends StatelessWidget {
|
||||
final String name;
|
||||
const _UserNameInput(
|
||||
this.name, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: TextEditingController()..text = name,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Name',
|
||||
),
|
||||
onSubmitted: (val) {
|
||||
context.read<SettingsUserViewBloc>().add(SettingsUserEvent.updateUserName(val));
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user