config menu user bloc

This commit is contained in:
appflowy 2021-07-26 11:51:39 +08:00
parent 9454c753c3
commit 9a1f17d41c
18 changed files with 229 additions and 141 deletions

View File

@ -1,6 +1,5 @@
import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_infra/time/duration.dart';
import 'package:flowy_infra_ui/widget/route/animation.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
import 'package:app_flowy/user/domain/i_auth.dart';

View File

@ -13,7 +13,7 @@ class WelcomeBloc extends Bloc<WelcomeEvent, WelcomeState> {
Stream<WelcomeState> mapEventToState(WelcomeEvent event) async* {
yield* event.map(
getUser: (val) async* {
final authState = await authImpl.currentUserState();
final authState = await authImpl.currentUserDetail();
yield state.copyWith(auth: authState);
},
);

View File

@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart';
import 'auth_state.dart';
abstract class IWelcomeAuth {
Future<AuthState> currentUserState();
Future<AuthState> currentUserDetail();
}
abstract class IWelcomeRoute {

View File

@ -13,7 +13,7 @@ export 'package:app_flowy/welcome/domain/i_welcome.dart';
class WelcomeAuthImpl implements IWelcomeAuth {
@override
Future<AuthState> currentUserState() {
Future<AuthState> currentUserDetail() {
final result = UserEventGetStatus().send();
return result.then((result) {
return result.fold(

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,14 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-user/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_detail.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
export 'package:flowy_sdk/protobuf/flowy-user/errors.pb.dart';
export 'package:flowy_sdk/protobuf/flowy-user/user_detail.pb.dart';
export 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
abstract class IUser {
Future<Either<Unit, WorkspaceError>> deleteWorkspace(String workspaceId);
Future<Either<UserDetail, UserError>> fetchUserDetail(String userId);
Future<Either<Unit, UserError>> signOut();
}

View File

@ -16,8 +16,10 @@ import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart';
import 'package:app_flowy/workspace/infrastructure/repos/view_repo.dart';
import 'package:app_flowy/workspace/infrastructure/repos/workspace_repo.dart';
import 'package:flowy_editor/flowy_editor.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_detail.pb.dart';
import 'package:get_it/get_it.dart';
import 'i_user_impl.dart';
import 'i_view_impl.dart';
class HomeDepsResolver {
@ -32,10 +34,10 @@ class HomeDepsResolver {
(appId, _) => IAppWatchImpl(repo: AppWatchRepository(appId: appId)));
//workspace
getIt.registerFactoryParam<IWorkspace, String, void>((workspaceId, _) =>
IWorkspaceImpl(repo: WorkspaceRepo(workspaceId: workspaceId)));
getIt.registerFactoryParam<IWorkspaceWatch, String, void>((workspacId, _) =>
IWorkspaceWatchImpl(repo: WorkspaceWatchRepo(workspaceId: workspacId)));
getIt.registerFactoryParam<IWorkspace, UserDetail, void>(
(user, _) => IWorkspaceImpl(repo: WorkspaceRepo(user: user)));
getIt.registerFactoryParam<IWorkspaceWatch, UserDetail, void>(
(user, _) => IWorkspaceWatchImpl(repo: WorkspaceWatchRepo(user: user)));
// View
getIt.registerFactoryParam<IView, String, void>(
@ -47,11 +49,15 @@ class HomeDepsResolver {
getIt.registerFactoryParam<IDoc, String, void>(
(docId, _) => IDocImpl(repo: DocRepository(docId: docId)));
// User
getIt.registerFactoryParam<IUser, UserDetail, void>(
(user, _) => IUserImpl(repo: UserRepo(user: user)));
//Bloc
getIt.registerFactoryParam<MenuBloc, String, void>(
(workspaceId, _) => MenuBloc(getIt<IWorkspace>(param1: workspaceId)));
getIt.registerFactoryParam<MenuWatchBloc, String, void>((workspaceId, _) =>
MenuWatchBloc(getIt<IWorkspaceWatch>(param1: workspaceId)));
getIt.registerFactoryParam<MenuBloc, UserDetail, void>(
(user, _) => MenuBloc(getIt<IWorkspace>(param1: user)));
getIt.registerFactoryParam<MenuWatchBloc, UserDetail, void>(
(user, _) => MenuWatchBloc(getIt<IWorkspaceWatch>(param1: user)));
getIt.registerFactoryParam<AppBloc, String, void>(
(appId, _) => AppBloc(getIt<IApp>(param1: appId)));

View File

@ -0,0 +1,30 @@
import 'package:dartz/dartz.dart';
import 'package:app_flowy/workspace/domain/i_user.dart';
import 'package:app_flowy/workspace/infrastructure/repos/user_repo.dart';
export 'package:app_flowy/workspace/domain/i_user.dart';
export 'package:app_flowy/workspace/infrastructure/repos/user_repo.dart';
class IUserImpl extends IUser {
UserRepo repo;
IUserImpl({
required this.repo,
});
@override
Future<Either<Unit, WorkspaceError>> deleteWorkspace(String workspaceId) {
// TODO: implement deleteWorkspace
throw UnimplementedError();
}
@override
Future<Either<UserDetail, UserError>> fetchUserDetail(String userId) {
return repo.fetchUserDetail(userId: userId);
}
@override
Future<Either<Unit, UserError>> signOut() {
// TODO: implement signOut
throw UnimplementedError();
}
}

View File

@ -0,0 +1,16 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-user/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_detail.pb.dart';
class UserRepo {
final UserDetail user;
UserRepo({
required this.user,
});
Future<Either<UserDetail, UserError>> fetchUserDetail(
{required String userId}) {
return UserEventGetStatus().send();
}
}

View File

@ -5,6 +5,7 @@ import 'package:dartz/dartz.dart';
import 'package:flowy_infra/flowy_logger.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-observable/subject.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_detail.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/observable.pb.dart';
@ -13,9 +14,9 @@ import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_query.pb.dart';
import 'package:flowy_sdk/rust_stream.dart';
class WorkspaceRepo {
String workspaceId;
UserDetail user;
WorkspaceRepo({
required this.workspaceId,
required this.user,
});
Future<Either<App, WorkspaceError>> createApp(String appName, String desc) {
@ -38,7 +39,7 @@ class WorkspaceRepo {
Future<Either<Workspace, WorkspaceError>> getWorkspace(
{bool readApps = false}) {
final request = QueryWorkspaceRequest.create()
..workspaceId = workspaceId
..workspaceId = user.workspace
..readApps = readApps;
return WorkspaceEventGetWorkspace(request).send().then((result) {
@ -54,13 +55,13 @@ class WorkspaceWatchRepo {
StreamSubscription<ObservableSubject>? _subscription;
WorkspaceAddAppCallback? _addAppCallback;
WorkspaceUpdatedCallback? _updatedCallback;
final String workspaceId;
final UserDetail user;
late WorkspaceRepo _repo;
WorkspaceWatchRepo({
required this.workspaceId,
required this.user,
}) {
_repo = WorkspaceRepo(workspaceId: workspaceId);
_repo = WorkspaceRepo(user: user);
}
void startWatching(
@ -70,7 +71,7 @@ class WorkspaceWatchRepo {
_updatedCallback = updatedCallback;
_subscription = RustStreamReceiver.listen((observable) {
if (observable.subjectId != workspaceId) {
if (observable.subjectId != user.workspace) {
return;
}

View File

@ -2,7 +2,6 @@ import 'package:app_flowy/workspace/application/app/app_bloc.dart';
import 'package:app_flowy/workspace/application/app/app_watch_bloc.dart';
import 'package:app_flowy/workspace/presentation/app/view_list.dart';
import 'package:app_flowy/workspace/presentation/widgets/menu/menu_list.dart';
import 'package:app_flowy/workspace/presentation/widgets/menu/menu_size.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:expandable/expandable.dart';
import 'package:flowy_infra/size.dart';
@ -15,7 +14,7 @@ import 'package:dartz/dartz.dart';
class AppWidget extends MenuItem {
final App app;
const AppWidget(this.app, {Key? key}) : super(key: key);
AppWidget(this.app, {Key? key}) : super(key: ValueKey(app.id));
@override
Widget build(BuildContext context) {
@ -103,11 +102,11 @@ class AppHeader extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ExpandableIcon(
theme: ExpandableThemeData(
theme: const ExpandableThemeData(
expandIcon: Icons.arrow_right,
collapseIcon: Icons.arrow_drop_down,
iconColor: Colors.black,
iconSize: HomeMenuSize.collapseIconSize,
iconSize: 24,
iconPadding: EdgeInsets.zero,
hasIcon: false,
),
@ -116,7 +115,7 @@ class AppHeader extends StatelessWidget {
child: Text(app.name),
),
SizedBox(
height: HomeMenuSize.createViewButtonSize,
height: 30,
child: createViewPopupMenu(context),
),
],

View File

@ -14,8 +14,8 @@ import 'home_layout.dart';
class HomeScreen extends StatelessWidget {
static GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
final UserDetail userDetail;
const HomeScreen(this.userDetail, {Key? key}) : super(key: key);
final UserDetail user;
const HomeScreen(this.user, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -74,7 +74,7 @@ class HomeScreen extends StatelessWidget {
isCollapseChanged: (isCollapse) {
homeBloc.add(HomeEvent.forceCollapse(isCollapse));
},
workspaceId: userDetail.workspace,
user: user,
);
homeMenu = RepaintBoundary(child: homeMenu);
homeMenu = FocusTraversalGroup(child: homeMenu);

View File

@ -1,32 +1,34 @@
import 'package:app_flowy/workspace/application/menu/menu_bloc.dart';
import 'package:app_flowy/workspace/application/menu/menu_watch.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/presentation/app/app_widget.dart';
import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
import 'package:app_flowy/workspace/presentation/widgets/menu/create_app_dialog.dart';
import 'package:app_flowy/workspace/presentation/widgets/menu/user_profile.dart';
import 'package:app_flowy/workspace/presentation/widgets/menu/menu_new_app.dart';
import 'package:app_flowy/workspace/presentation/widgets/menu/menu_top_bar.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_detail.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/menu/menu_bloc.dart';
import 'package:app_flowy/workspace/application/menu/menu_watch.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/workspace/presentation/app/app_widget.dart';
import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
import 'package:app_flowy/workspace/presentation/widgets/menu/menu_user.dart';
import 'menu_list.dart';
class HomeMenu extends StatelessWidget {
final Function(HomeStackView?) pageContextChanged;
final Function(bool) isCollapseChanged;
final String workspaceId;
final UserDetail user;
const HomeMenu(
{Key? key,
required this.pageContextChanged,
required this.isCollapseChanged,
required this.workspaceId})
required this.user})
: super(key: key);
@override
@ -34,10 +36,10 @@ class HomeMenu extends StatelessWidget {
return MultiBlocProvider(
providers: [
BlocProvider<MenuBloc>(
create: (context) => getIt<MenuBloc>(param1: workspaceId)
..add(const MenuEvent.initial())),
create: (context) =>
getIt<MenuBloc>(param1: user)..add(const MenuEvent.initial())),
BlocProvider(
create: (context) => getIt<MenuWatchBloc>(param1: workspaceId)
create: (context) => getIt<MenuWatchBloc>(param1: user)
..add(const MenuWatchEvent.started())),
],
child: MultiBlocListener(
@ -84,12 +86,12 @@ class HomeMenu extends StatelessWidget {
return BlocBuilder<MenuWatchBloc, MenuWatchState>(
builder: (context, state) {
return state.map(
initial: (_) => BlocBuilder<MenuBloc, MenuState>(
builder: (context, s) => MenuList(
menuItems: menuItemsWithApps(s.apps),
),
initial: (_) => MenuList(
menuItems: menuItemsWithApps(context.read<MenuBloc>().state.apps),
),
loadApps: (s) => MenuList(
menuItems: menuItemsWithApps(some(s.apps)),
),
loadApps: (s) => MenuList(menuItems: menuItemsWithApps(some(s.apps))),
loadFail: (s) => FlowyErrorPage(s.error.toString()),
);
},
@ -111,94 +113,30 @@ class HomeMenu extends StatelessWidget {
}
List<MenuItem> menuItemsWithApps(Option<List<App>> someApps) {
List<MenuItem> menuItems = [
const UserProfile(),
];
// apps
List<MenuItem> appWidgets = someApps.fold(
() => [],
(apps) => apps.map((app) => AppWidget(app)).toList(),
);
menuItems.addAll(appWidgets);
return menuItems;
return MenuItemBuilder().withUser(user).withApps(someApps).build();
}
}
class MenuTopBar extends StatelessWidget {
const MenuTopBar({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<MenuBloc, MenuState>(
builder: (context, state) {
return Row(
children: [
const Image(
fit: BoxFit.cover,
width: 25,
height: 25,
image: AssetImage('assets/images/app_flowy_logo.jpg')),
const HSpace(8),
const Text(
'AppFlowy',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
const Spacer(),
IconButton(
icon: const Icon(Icons.arrow_left),
alignment: Alignment.centerRight,
padding: EdgeInsets.zero,
onPressed: () =>
context.read<MenuBloc>().add(const MenuEvent.collapse()),
),
],
);
},
class MenuItemBuilder {
List<MenuItem> items = [];
MenuItemBuilder();
MenuItemBuilder withUser(UserDetail user) {
items.add(MenuUser(user));
return this;
}
MenuItemBuilder withApps(Option<List<App>> someApps) {
List<MenuItem> appWidgets = someApps.foldRight(
[],
(apps, _) => apps.map((app) => AppWidget(app)).toList(),
);
}
}
class NewAppButton extends StatelessWidget {
final Function(String)? press;
const NewAppButton({this.press, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(width: 1, color: Colors.grey.shade300),
),
),
height: HomeSizes.menuAddButtonHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Icon(Icons.add_circle_rounded, size: 30),
TextButton(
onPressed: () async => await _showCreateAppDialog(context),
child: const Text(
'New App',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
)
],
).padding(horizontal: Insets.l),
);
}
Future<void> _showCreateAppDialog(BuildContext context) async {
await Dialogs.showWithContext(CreateAppDialogContext(
confirm: (appName) {
if (appName.isNotEmpty && press != null) {
press!(appName);
}
},
), context);
items.addAll(appWidgets);
return this;
}
List<MenuItem> build() {
return items;
}
}

View File

@ -0,0 +1,50 @@
import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
import 'package:app_flowy/workspace/presentation/widgets/menu/create_app_dialog.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
import 'package:flutter/material.dart';
import 'package:styled_widget/styled_widget.dart';
class NewAppButton extends StatelessWidget {
final Function(String)? press;
const NewAppButton({this.press, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(width: 1, color: Colors.grey.shade300),
),
),
height: HomeSizes.menuAddButtonHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Icon(Icons.add_circle_rounded, size: 30),
TextButton(
onPressed: () async => await _showCreateAppDialog(context),
child: const Text(
'New App',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
)
],
).padding(horizontal: Insets.l),
);
}
Future<void> _showCreateAppDialog(BuildContext context) async {
await Dialogs.showWithContext(CreateAppDialogContext(
confirm: (appName) {
if (appName.isNotEmpty && press != null) {
press!(appName);
}
},
), context);
}
}

View File

@ -1,4 +0,0 @@
class HomeMenuSize {
static double get createViewButtonSize => 30;
static double get collapseIconSize => 24;
}

View File

@ -0,0 +1,37 @@
import 'package:app_flowy/workspace/application/menu/menu_bloc.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MenuTopBar extends StatelessWidget {
const MenuTopBar({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<MenuBloc, MenuState>(
builder: (context, state) {
return Row(
children: [
const Image(
fit: BoxFit.cover,
width: 25,
height: 25,
image: AssetImage('assets/images/app_flowy_logo.jpg')),
const HSpace(8),
const Text(
'AppFlowy',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
const Spacer(),
IconButton(
icon: const Icon(Icons.arrow_left),
alignment: Alignment.centerRight,
padding: EdgeInsets.zero,
onPressed: () =>
context.read<MenuBloc>().add(const MenuEvent.collapse()),
),
],
);
},
);
}
}

View File

@ -1,9 +1,11 @@
import 'package:app_flowy/workspace/presentation/widgets/menu/menu_list.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_detail.pb.dart';
import 'package:flutter/material.dart';
class UserProfile extends MenuItem {
const UserProfile({Key? key}) : super(key: key);
class MenuUser extends MenuItem {
final UserDetail user;
MenuUser(this.user, {Key? key}) : super(key: ValueKey(user.id));
@override
Widget build(BuildContext context) {
@ -12,11 +14,11 @@ class UserProfile extends MenuItem {
width: 30,
height: 30,
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(10),
child: const Image(image: AssetImage('assets/images/avatar.jpg')),
),
),
const HSpace(6),
const HSpace(10),
const Text("nathan", style: TextStyle(fontSize: 18)),
]);
}

View File

@ -1,2 +1 @@
export 'menu.dart';
export 'menu_size.dart';