浏览代码

config menu user bloc

appflowy 3 年之前
父节点
当前提交
9a1f17d41c

+ 0 - 1
app_flowy/lib/user/infrastructure/i_auth_impl.dart

@@ -1,6 +1,5 @@
 import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
 import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
 import 'package:dartz/dartz.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_infra_ui/widget/route/animation.dart';
 import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
 import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
 import 'package:app_flowy/user/domain/i_auth.dart';
 import 'package:app_flowy/user/domain/i_auth.dart';

+ 1 - 1
app_flowy/lib/welcome/application/welcome_bloc.dart

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

+ 1 - 1
app_flowy/lib/welcome/domain/i_welcome.dart

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

+ 1 - 1
app_flowy/lib/welcome/infrastructure/i_welcome_impl.dart

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

+ 1 - 0
app_flowy/lib/workspace/application/menu/menu_user_bloc.dart

@@ -0,0 +1 @@
+

+ 14 - 0
app_flowy/lib/workspace/domain/i_user.dart

@@ -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();
+}

+ 14 - 8
app_flowy/lib/workspace/infrastructure/deps_resolver.dart

@@ -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/view_repo.dart';
 import 'package:app_flowy/workspace/infrastructure/repos/workspace_repo.dart';
 import 'package:app_flowy/workspace/infrastructure/repos/workspace_repo.dart';
 import 'package:flowy_editor/flowy_editor.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 'package:get_it/get_it.dart';
 
 
+import 'i_user_impl.dart';
 import 'i_view_impl.dart';
 import 'i_view_impl.dart';
 
 
 class HomeDepsResolver {
 class HomeDepsResolver {
@@ -32,10 +34,10 @@ class HomeDepsResolver {
         (appId, _) => IAppWatchImpl(repo: AppWatchRepository(appId: appId)));
         (appId, _) => IAppWatchImpl(repo: AppWatchRepository(appId: appId)));
 
 
     //workspace
     //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
     // View
     getIt.registerFactoryParam<IView, String, void>(
     getIt.registerFactoryParam<IView, String, void>(
@@ -47,11 +49,15 @@ class HomeDepsResolver {
     getIt.registerFactoryParam<IDoc, String, void>(
     getIt.registerFactoryParam<IDoc, String, void>(
         (docId, _) => IDocImpl(repo: DocRepository(docId: docId)));
         (docId, _) => IDocImpl(repo: DocRepository(docId: docId)));
 
 
+    // User
+    getIt.registerFactoryParam<IUser, UserDetail, void>(
+        (user, _) => IUserImpl(repo: UserRepo(user: user)));
+
     //Bloc
     //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>(
     getIt.registerFactoryParam<AppBloc, String, void>(
         (appId, _) => AppBloc(getIt<IApp>(param1: appId)));
         (appId, _) => AppBloc(getIt<IApp>(param1: appId)));

+ 30 - 0
app_flowy/lib/workspace/infrastructure/i_user_impl.dart

@@ -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();
+  }
+}

+ 16 - 0
app_flowy/lib/workspace/infrastructure/repos/user_repo.dart

@@ -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();
+  }
+}

+ 8 - 7
app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart

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

+ 4 - 5
app_flowy/lib/workspace/presentation/app/app_widget.dart

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

+ 3 - 3
app_flowy/lib/workspace/presentation/home/home_screen.dart

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

+ 39 - 101
app_flowy/lib/workspace/presentation/widgets/menu/menu.dart

@@ -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:dartz/dartz.dart';
 import 'package:flowy_infra/size.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/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:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:styled_widget/styled_widget.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';
 import 'menu_list.dart';
 
 
 class HomeMenu extends StatelessWidget {
 class HomeMenu extends StatelessWidget {
   final Function(HomeStackView?) pageContextChanged;
   final Function(HomeStackView?) pageContextChanged;
   final Function(bool) isCollapseChanged;
   final Function(bool) isCollapseChanged;
-  final String workspaceId;
+  final UserDetail user;
 
 
   const HomeMenu(
   const HomeMenu(
       {Key? key,
       {Key? key,
       required this.pageContextChanged,
       required this.pageContextChanged,
       required this.isCollapseChanged,
       required this.isCollapseChanged,
-      required this.workspaceId})
+      required this.user})
       : super(key: key);
       : super(key: key);
 
 
   @override
   @override
@@ -34,10 +36,10 @@ class HomeMenu extends StatelessWidget {
     return MultiBlocProvider(
     return MultiBlocProvider(
       providers: [
       providers: [
         BlocProvider<MenuBloc>(
         BlocProvider<MenuBloc>(
-            create: (context) => getIt<MenuBloc>(param1: workspaceId)
-              ..add(const MenuEvent.initial())),
+            create: (context) =>
+                getIt<MenuBloc>(param1: user)..add(const MenuEvent.initial())),
         BlocProvider(
         BlocProvider(
-            create: (context) => getIt<MenuWatchBloc>(param1: workspaceId)
+            create: (context) => getIt<MenuWatchBloc>(param1: user)
               ..add(const MenuWatchEvent.started())),
               ..add(const MenuWatchEvent.started())),
       ],
       ],
       child: MultiBlocListener(
       child: MultiBlocListener(
@@ -84,12 +86,12 @@ class HomeMenu extends StatelessWidget {
     return BlocBuilder<MenuWatchBloc, MenuWatchState>(
     return BlocBuilder<MenuWatchBloc, MenuWatchState>(
       builder: (context, state) {
       builder: (context, state) {
         return state.map(
         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()),
           loadFail: (s) => FlowyErrorPage(s.error.toString()),
         );
         );
       },
       },
@@ -111,94 +113,30 @@ class HomeMenu extends StatelessWidget {
   }
   }
 
 
   List<MenuItem> menuItemsWithApps(Option<List<App>> someApps) {
   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 = [];
 
 
-class NewAppButton extends StatelessWidget {
-  final Function(String)? press;
+  MenuItemBuilder();
 
 
-  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),
+  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(),
     );
     );
+    items.addAll(appWidgets);
+    return this;
   }
   }
 
 
-  Future<void> _showCreateAppDialog(BuildContext context) async {
-    await Dialogs.showWithContext(CreateAppDialogContext(
-      confirm: (appName) {
-        if (appName.isNotEmpty && press != null) {
-          press!(appName);
-        }
-      },
-    ), context);
+  List<MenuItem> build() {
+    return items;
   }
   }
 }
 }

+ 50 - 0
app_flowy/lib/workspace/presentation/widgets/menu/menu_new_app.dart

@@ -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);
+  }
+}

+ 0 - 4
app_flowy/lib/workspace/presentation/widgets/menu/menu_size.dart

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

+ 37 - 0
app_flowy/lib/workspace/presentation/widgets/menu/menu_top_bar.dart

@@ -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()),
+            ),
+          ],
+        );
+      },
+    );
+  }
+}

+ 6 - 4
app_flowy/lib/workspace/presentation/widgets/menu/user_profile.dart → app_flowy/lib/workspace/presentation/widgets/menu/menu_user.dart

@@ -1,9 +1,11 @@
 import 'package:app_flowy/workspace/presentation/widgets/menu/menu_list.dart';
 import 'package:app_flowy/workspace/presentation/widgets/menu/menu_list.dart';
 import 'package:flowy_infra_ui/widget/spacing.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';
 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
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
@@ -12,11 +14,11 @@ class UserProfile extends MenuItem {
         width: 30,
         width: 30,
         height: 30,
         height: 30,
         child: ClipRRect(
         child: ClipRRect(
-          borderRadius: BorderRadius.circular(8),
+          borderRadius: BorderRadius.circular(10),
           child: const Image(image: AssetImage('assets/images/avatar.jpg')),
           child: const Image(image: AssetImage('assets/images/avatar.jpg')),
         ),
         ),
       ),
       ),
-      const HSpace(6),
+      const HSpace(10),
       const Text("nathan", style: TextStyle(fontSize: 18)),
       const Text("nathan", style: TextStyle(fontSize: 18)),
     ]);
     ]);
   }
   }

+ 0 - 1
app_flowy/lib/workspace/presentation/widgets/menu/prelude.dart

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