Переглянути джерело

Merge branch 'main' into infra_ui/overlay_base

Jaylen Bian 4 роки тому
батько
коміт
ae45e64244
100 змінених файлів з 6571 додано та 1496 видалено
  1. 1 0
      app_flowy/analysis_options.yaml
  2. 1 1
      app_flowy/ios/Flutter/AppFrameworkInfo.plist
  3. 0 9
      app_flowy/lib/home/application/edit_pannel/edit_pannel_event.dart
  4. 0 14
      app_flowy/lib/home/application/edit_pannel/edit_pannel_state.dart
  5. 0 42
      app_flowy/lib/home/application/home_bloc.dart
  6. 0 15
      app_flowy/lib/home/application/home_event.dart
  7. 0 18
      app_flowy/lib/home/application/home_state.dart
  8. 0 44
      app_flowy/lib/home/application/menu/menu_bloc.dart
  9. 0 525
      app_flowy/lib/home/application/menu/menu_bloc.freezed.dart
  10. 0 8
      app_flowy/lib/home/application/menu/menu_event.dart
  11. 0 14
      app_flowy/lib/home/application/menu/menu_state.dart
  12. 0 7
      app_flowy/lib/home/application/watcher/home_watcher_event.dart
  13. 0 7
      app_flowy/lib/home/application/watcher/home_watcher_state.dart
  14. 0 19
      app_flowy/lib/home/domain/page_context.dart
  15. 0 121
      app_flowy/lib/home/presentation/widgets/menu/home_menu.dart
  16. 0 2
      app_flowy/lib/home/presentation/widgets/menu/prelude.dart
  17. 1 1
      app_flowy/lib/main.dart
  18. 4 2
      app_flowy/lib/startup/deps_inject/prelude.dart
  19. 1 1
      app_flowy/lib/startup/startup.dart
  20. 3 3
      app_flowy/lib/startup/tasks/application_task.dart
  21. 1 1
      app_flowy/lib/startup/tasks/prelude.dart
  22. 2 1
      app_flowy/lib/startup/tasks/rust_sdk_init_task.dart
  23. 25 3
      app_flowy/lib/user/application/sign_in/sign_in_bloc.dart
  24. 0 10
      app_flowy/lib/user/application/sign_in/sign_in_event.dart
  25. 0 16
      app_flowy/lib/user/application/sign_in/sign_in_state.dart
  26. 0 0
      app_flowy/lib/user/domain/i_auth.dart
  27. 17 0
      app_flowy/lib/user/infrastructure/deps_resolver.dart
  28. 2 17
      app_flowy/lib/user/infrastructure/i_auth_impl.dart
  29. 0 0
      app_flowy/lib/user/infrastructure/repos/auth_repo.dart
  30. 1 1
      app_flowy/lib/user/presentation/sign_in/widgets/body.dart
  31. 17 3
      app_flowy/lib/welcome/application/welcome_bloc.dart
  32. 0 6
      app_flowy/lib/welcome/application/welcome_event.dart
  33. 0 12
      app_flowy/lib/welcome/application/welcome_state.dart
  34. 0 0
      app_flowy/lib/welcome/domain/i_welcome.dart
  35. 19 0
      app_flowy/lib/welcome/infrastructure/deps_resolver.dart
  36. 39 0
      app_flowy/lib/welcome/infrastructure/i_welcome_impl.dart
  37. 0 58
      app_flowy/lib/welcome/infrastructure/interface_impl.dart
  38. 1 1
      app_flowy/lib/welcome/presentation/welcome_screen.dart
  39. 58 0
      app_flowy/lib/workspace/application/app/app_bloc.dart
  40. 484 0
      app_flowy/lib/workspace/application/app/app_bloc.freezed.dart
  41. 56 0
      app_flowy/lib/workspace/application/app/app_watch_bloc.dart
  42. 683 0
      app_flowy/lib/workspace/application/app/app_watch_bloc.freezed.dart
  43. 39 0
      app_flowy/lib/workspace/application/doc/doc_bloc.dart
  44. 498 0
      app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart
  45. 22 3
      app_flowy/lib/workspace/application/edit_pannel/edit_pannel_bloc.dart
  46. 0 0
      app_flowy/lib/workspace/application/edit_pannel/edit_pannel_bloc.freezed.dart
  47. 58 0
      app_flowy/lib/workspace/application/home/home_bloc.dart
  48. 81 256
      app_flowy/lib/workspace/application/home/home_bloc.freezed.dart
  49. 12 3
      app_flowy/lib/workspace/application/home/home_watcher_bloc.dart
  50. 0 0
      app_flowy/lib/workspace/application/home/home_watcher_bloc.freezed.dart
  51. 86 0
      app_flowy/lib/workspace/application/menu/menu_bloc.dart
  52. 739 0
      app_flowy/lib/workspace/application/menu/menu_bloc.freezed.dart
  53. 64 0
      app_flowy/lib/workspace/application/menu/menu_watch.dart
  54. 682 0
      app_flowy/lib/workspace/application/menu/menu_watch.freezed.dart
  55. 42 0
      app_flowy/lib/workspace/application/view/doc_watch_bloc.dart
  56. 542 0
      app_flowy/lib/workspace/application/view/doc_watch_bloc.freezed.dart
  57. 44 0
      app_flowy/lib/workspace/application/view/view_bloc.dart
  58. 327 0
      app_flowy/lib/workspace/application/view/view_bloc.freezed.dart
  59. 0 0
      app_flowy/lib/workspace/domain/edit_context.dart
  60. 21 0
      app_flowy/lib/workspace/domain/i_app.dart
  61. 18 0
      app_flowy/lib/workspace/domain/i_doc.dart
  62. 15 0
      app_flowy/lib/workspace/domain/i_view.dart
  63. 21 0
      app_flowy/lib/workspace/domain/i_workspace.dart
  64. 90 0
      app_flowy/lib/workspace/domain/page_stack/page_stack.dart
  65. 35 0
      app_flowy/lib/workspace/domain/page_stack/page_stack_bloc.dart
  66. 337 0
      app_flowy/lib/workspace/domain/page_stack/page_stack_bloc.freezed.dart
  67. 72 0
      app_flowy/lib/workspace/infrastructure/deps_resolver.dart
  68. 65 0
      app_flowy/lib/workspace/infrastructure/i_app_impl.dart
  69. 51 0
      app_flowy/lib/workspace/infrastructure/i_doc_impl.dart
  70. 33 0
      app_flowy/lib/workspace/infrastructure/i_view_impl.dart
  71. 50 0
      app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart
  72. 115 0
      app_flowy/lib/workspace/infrastructure/repos/app_repo.dart
  73. 45 0
      app_flowy/lib/workspace/infrastructure/repos/doc_repo.dart
  74. 75 0
      app_flowy/lib/workspace/infrastructure/repos/view_repo.dart
  75. 116 0
      app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart
  76. 149 0
      app_flowy/lib/workspace/presentation/app/app_widget.dart
  77. 0 0
      app_flowy/lib/workspace/presentation/app/new_app.dart
  78. 45 0
      app_flowy/lib/workspace/presentation/app/view_list.dart
  79. 51 0
      app_flowy/lib/workspace/presentation/doc/doc_page.dart
  80. 61 0
      app_flowy/lib/workspace/presentation/doc/editor_widget.dart
  81. 8 8
      app_flowy/lib/workspace/presentation/home/home_layout.dart
  82. 45 63
      app_flowy/lib/workspace/presentation/home/home_screen.dart
  83. 0 0
      app_flowy/lib/workspace/presentation/home/home_sizes.dart
  84. 39 0
      app_flowy/lib/workspace/presentation/view/view_widget.dart
  85. 7 6
      app_flowy/lib/workspace/presentation/widgets/blank_page.dart
  86. 3 3
      app_flowy/lib/workspace/presentation/widgets/edit_pannel/edit_pannel.dart
  87. 0 0
      app_flowy/lib/workspace/presentation/widgets/edit_pannel/pannel_animation.dart
  88. 0 0
      app_flowy/lib/workspace/presentation/widgets/fading_index_stack.dart
  89. 2 2
      app_flowy/lib/workspace/presentation/widgets/home_stack_page.dart
  90. 7 8
      app_flowy/lib/workspace/presentation/widgets/home_top_bar.dart
  91. 29 0
      app_flowy/lib/workspace/presentation/widgets/menu/app_list.dart
  92. 221 0
      app_flowy/lib/workspace/presentation/widgets/menu/menu.dart
  93. 0 0
      app_flowy/lib/workspace/presentation/widgets/menu/menu_size.dart
  94. 2 0
      app_flowy/lib/workspace/presentation/widgets/menu/prelude.dart
  95. 1 1
      app_flowy/lib/workspace/presentation/widgets/prelude.dart
  96. 1 1
      app_flowy/macos/Podfile.lock
  97. 13 19
      app_flowy/packages/flowy_editor/lib/src/service/controller.dart
  98. 4 2
      app_flowy/packages/flowy_editor/lib/src/service/keyboard.dart
  99. 68 135
      app_flowy/packages/flowy_editor/lib/src/widget/raw_editor.dart
  100. 4 4
      app_flowy/packages/flowy_infra/pubspec.lock

+ 1 - 0
app_flowy/analysis_options.yaml

@@ -15,6 +15,7 @@ analyzer:
     - "**/*.g.dart"
     - "**/*.freezed.dart"
     - "packages/flowy_editor/**"
+    - "packages/flowy_infra_ui/**"
     
 linter:
   # The lint rules applied to this project can be customized in the

+ 1 - 1
app_flowy/ios/Flutter/AppFrameworkInfo.plist

@@ -21,6 +21,6 @@
   <key>CFBundleVersion</key>
   <string>1.0</string>
   <key>MinimumOSVersion</key>
-  <string>8.0</string>
+  <string>9.0</string>
 </dict>
 </plist>

+ 0 - 9
app_flowy/lib/home/application/edit_pannel/edit_pannel_event.dart

@@ -1,9 +0,0 @@
-part of 'edit_pannel_bloc.dart';
-
-@freezed
-abstract class EditPannelEvent with _$EditPannelEvent {
-  const factory EditPannelEvent.startEdit(EditPannelContext context) =
-      _StartEdit;
-
-  const factory EditPannelEvent.endEdit(EditPannelContext context) = _EndEdit;
-}

+ 0 - 14
app_flowy/lib/home/application/edit_pannel/edit_pannel_state.dart

@@ -1,14 +0,0 @@
-part of 'edit_pannel_bloc.dart';
-
-@freezed
-abstract class EditPannelState implements _$EditPannelState {
-  const factory EditPannelState({
-    required bool isEditing,
-    required Option<EditPannelContext> editContext,
-  }) = _EditPannelState;
-
-  factory EditPannelState.initial() => EditPannelState(
-        isEditing: false,
-        editContext: none(),
-      );
-}

+ 0 - 42
app_flowy/lib/home/application/home_bloc.dart

@@ -1,42 +0,0 @@
-import 'package:app_flowy/home/domain/edit_context.dart';
-import 'package:app_flowy/home/domain/page_context.dart';
-import 'package:app_flowy/home/presentation/widgets/blank_page.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:dartz/dartz.dart';
-
-part 'home_event.dart';
-part 'home_state.dart';
-part 'home_bloc.freezed.dart';
-
-class HomeBloc extends Bloc<HomeEvent, HomeState> {
-  HomeBloc() : super(HomeState.initial());
-
-  @override
-  Stream<HomeState> mapEventToState(
-    HomeEvent event,
-  ) async* {
-    yield* event.map(
-      setPage: (e) async* {
-        yield state.copyWith(pageContext: e.context);
-      },
-      showLoading: (e) async* {
-        yield state.copyWith(isLoading: e.isLoading);
-      },
-      setEditPannel: (e) async* {
-        yield state.copyWith(editContext: some(e.editContext));
-      },
-      dismissEditPannel: (value) async* {
-        yield state.copyWith(editContext: none());
-      },
-      showMenu: (e) async* {
-        yield state.copyWith(showMenu: e.isShow);
-      },
-    );
-  }
-
-  @override
-  Future<void> close() {
-    return super.close();
-  }
-}

+ 0 - 15
app_flowy/lib/home/application/home_event.dart

@@ -1,15 +0,0 @@
-part of 'home_bloc.dart';
-
-@freezed
-abstract class HomeEvent with _$HomeEvent {
-  const factory HomeEvent.showLoading(bool isLoading) = _ShowLoading;
-  const factory HomeEvent.showMenu(bool isShow) = _ShowMenu;
-
-  //page
-  const factory HomeEvent.setPage(PageContext context) = SetCurrentPage;
-
-  //edit pannel
-  const factory HomeEvent.setEditPannel(EditPannelContext editContext) =
-      _ShowEditPannel;
-  const factory HomeEvent.dismissEditPannel() = _DismissEditPannel;
-}

+ 0 - 18
app_flowy/lib/home/application/home_state.dart

@@ -1,18 +0,0 @@
-part of 'home_bloc.dart';
-
-@freezed
-abstract class HomeState implements _$HomeState {
-  const factory HomeState({
-    required bool isLoading,
-    required bool showMenu,
-    required PageContext pageContext,
-    required Option<EditPannelContext> editContext,
-  }) = _HomeState;
-
-  factory HomeState.initial() => HomeState(
-        isLoading: false,
-        showMenu: true,
-        pageContext: const BlankPageContext(),
-        editContext: none(),
-      );
-}

+ 0 - 44
app_flowy/lib/home/application/menu/menu_bloc.dart

@@ -1,44 +0,0 @@
-import 'dart:async';
-import 'package:app_flowy/home/domain/page_context.dart';
-import 'package:dartz/dartz.dart';
-import 'package:flutter/material.dart';
-import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-part 'menu_event.dart';
-part 'menu_state.dart';
-part 'menu_bloc.freezed.dart';
-
-class MenuBloc extends Bloc<MenuEvent, MenuState> {
-  MenuBloc() : super(MenuState.initial());
-
-  @override
-  Stream<MenuState> mapEventToState(
-    MenuEvent event,
-  ) async* {
-    yield* event.map(
-      collapse: (e) async* {
-        final isCollapse = state.isCollapse;
-        yield state.copyWith(isCollapse: !isCollapse);
-      },
-      openPage: (e) async* {
-        yield* _performActionOnOpenPage(e);
-      },
-      createApp: (e) async* {
-        yield* _performActionOnCreateApp(e);
-      },
-    );
-  }
-
-  Stream<MenuState> _performActionOnOpenPage(_OpenPage e) async* {
-    yield state.copyWith(pageContext: some(e.context));
-  }
-
-  Stream<MenuState> _performActionOnCreateApp(_CreateApp e) async* {
-    yield state;
-  }
-
-  @override
-  Future<void> close() {
-    return super.close();
-  }
-}

+ 0 - 525
app_flowy/lib/home/application/menu/menu_bloc.freezed.dart

@@ -1,525 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
-
-part of 'menu_bloc.dart';
-
-// **************************************************************************
-// FreezedGenerator
-// **************************************************************************
-
-T _$identity<T>(T value) => value;
-
-final _privateConstructorUsedError = UnsupportedError(
-    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
-
-/// @nodoc
-class _$MenuEventTearOff {
-  const _$MenuEventTearOff();
-
-  Collapse collapse() {
-    return const Collapse();
-  }
-
-  _OpenPage openPage(PageContext context) {
-    return _OpenPage(
-      context,
-    );
-  }
-
-  _CreateApp createApp() {
-    return const _CreateApp();
-  }
-}
-
-/// @nodoc
-const $MenuEvent = _$MenuEventTearOff();
-
-/// @nodoc
-mixin _$MenuEvent {
-  @optionalTypeArgs
-  TResult when<TResult extends Object?>({
-    required TResult Function() collapse,
-    required TResult Function(PageContext context) openPage,
-    required TResult Function() createApp,
-  }) =>
-      throw _privateConstructorUsedError;
-  @optionalTypeArgs
-  TResult maybeWhen<TResult extends Object?>({
-    TResult Function()? collapse,
-    TResult Function(PageContext context)? openPage,
-    TResult Function()? createApp,
-    required TResult orElse(),
-  }) =>
-      throw _privateConstructorUsedError;
-  @optionalTypeArgs
-  TResult map<TResult extends Object?>({
-    required TResult Function(Collapse value) collapse,
-    required TResult Function(_OpenPage value) openPage,
-    required TResult Function(_CreateApp value) createApp,
-  }) =>
-      throw _privateConstructorUsedError;
-  @optionalTypeArgs
-  TResult maybeMap<TResult extends Object?>({
-    TResult Function(Collapse value)? collapse,
-    TResult Function(_OpenPage value)? openPage,
-    TResult Function(_CreateApp value)? createApp,
-    required TResult orElse(),
-  }) =>
-      throw _privateConstructorUsedError;
-}
-
-/// @nodoc
-abstract class $MenuEventCopyWith<$Res> {
-  factory $MenuEventCopyWith(MenuEvent value, $Res Function(MenuEvent) then) =
-      _$MenuEventCopyWithImpl<$Res>;
-}
-
-/// @nodoc
-class _$MenuEventCopyWithImpl<$Res> implements $MenuEventCopyWith<$Res> {
-  _$MenuEventCopyWithImpl(this._value, this._then);
-
-  final MenuEvent _value;
-  // ignore: unused_field
-  final $Res Function(MenuEvent) _then;
-}
-
-/// @nodoc
-abstract class $CollapseCopyWith<$Res> {
-  factory $CollapseCopyWith(Collapse value, $Res Function(Collapse) then) =
-      _$CollapseCopyWithImpl<$Res>;
-}
-
-/// @nodoc
-class _$CollapseCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
-    implements $CollapseCopyWith<$Res> {
-  _$CollapseCopyWithImpl(Collapse _value, $Res Function(Collapse) _then)
-      : super(_value, (v) => _then(v as Collapse));
-
-  @override
-  Collapse get _value => super._value as Collapse;
-}
-
-/// @nodoc
-
-class _$Collapse implements Collapse {
-  const _$Collapse();
-
-  @override
-  String toString() {
-    return 'MenuEvent.collapse()';
-  }
-
-  @override
-  bool operator ==(dynamic other) {
-    return identical(this, other) || (other is Collapse);
-  }
-
-  @override
-  int get hashCode => runtimeType.hashCode;
-
-  @override
-  @optionalTypeArgs
-  TResult when<TResult extends Object?>({
-    required TResult Function() collapse,
-    required TResult Function(PageContext context) openPage,
-    required TResult Function() createApp,
-  }) {
-    return collapse();
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeWhen<TResult extends Object?>({
-    TResult Function()? collapse,
-    TResult Function(PageContext context)? openPage,
-    TResult Function()? createApp,
-    required TResult orElse(),
-  }) {
-    if (collapse != null) {
-      return collapse();
-    }
-    return orElse();
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult map<TResult extends Object?>({
-    required TResult Function(Collapse value) collapse,
-    required TResult Function(_OpenPage value) openPage,
-    required TResult Function(_CreateApp value) createApp,
-  }) {
-    return collapse(this);
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeMap<TResult extends Object?>({
-    TResult Function(Collapse value)? collapse,
-    TResult Function(_OpenPage value)? openPage,
-    TResult Function(_CreateApp value)? createApp,
-    required TResult orElse(),
-  }) {
-    if (collapse != null) {
-      return collapse(this);
-    }
-    return orElse();
-  }
-}
-
-abstract class Collapse implements MenuEvent {
-  const factory Collapse() = _$Collapse;
-}
-
-/// @nodoc
-abstract class _$OpenPageCopyWith<$Res> {
-  factory _$OpenPageCopyWith(_OpenPage value, $Res Function(_OpenPage) then) =
-      __$OpenPageCopyWithImpl<$Res>;
-  $Res call({PageContext context});
-}
-
-/// @nodoc
-class __$OpenPageCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
-    implements _$OpenPageCopyWith<$Res> {
-  __$OpenPageCopyWithImpl(_OpenPage _value, $Res Function(_OpenPage) _then)
-      : super(_value, (v) => _then(v as _OpenPage));
-
-  @override
-  _OpenPage get _value => super._value as _OpenPage;
-
-  @override
-  $Res call({
-    Object? context = freezed,
-  }) {
-    return _then(_OpenPage(
-      context == freezed
-          ? _value.context
-          : context // ignore: cast_nullable_to_non_nullable
-              as PageContext,
-    ));
-  }
-}
-
-/// @nodoc
-
-class _$_OpenPage implements _OpenPage {
-  const _$_OpenPage(this.context);
-
-  @override
-  final PageContext context;
-
-  @override
-  String toString() {
-    return 'MenuEvent.openPage(context: $context)';
-  }
-
-  @override
-  bool operator ==(dynamic other) {
-    return identical(this, other) ||
-        (other is _OpenPage &&
-            (identical(other.context, context) ||
-                const DeepCollectionEquality().equals(other.context, context)));
-  }
-
-  @override
-  int get hashCode =>
-      runtimeType.hashCode ^ const DeepCollectionEquality().hash(context);
-
-  @JsonKey(ignore: true)
-  @override
-  _$OpenPageCopyWith<_OpenPage> get copyWith =>
-      __$OpenPageCopyWithImpl<_OpenPage>(this, _$identity);
-
-  @override
-  @optionalTypeArgs
-  TResult when<TResult extends Object?>({
-    required TResult Function() collapse,
-    required TResult Function(PageContext context) openPage,
-    required TResult Function() createApp,
-  }) {
-    return openPage(context);
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeWhen<TResult extends Object?>({
-    TResult Function()? collapse,
-    TResult Function(PageContext context)? openPage,
-    TResult Function()? createApp,
-    required TResult orElse(),
-  }) {
-    if (openPage != null) {
-      return openPage(context);
-    }
-    return orElse();
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult map<TResult extends Object?>({
-    required TResult Function(Collapse value) collapse,
-    required TResult Function(_OpenPage value) openPage,
-    required TResult Function(_CreateApp value) createApp,
-  }) {
-    return openPage(this);
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeMap<TResult extends Object?>({
-    TResult Function(Collapse value)? collapse,
-    TResult Function(_OpenPage value)? openPage,
-    TResult Function(_CreateApp value)? createApp,
-    required TResult orElse(),
-  }) {
-    if (openPage != null) {
-      return openPage(this);
-    }
-    return orElse();
-  }
-}
-
-abstract class _OpenPage implements MenuEvent {
-  const factory _OpenPage(PageContext context) = _$_OpenPage;
-
-  PageContext get context => throw _privateConstructorUsedError;
-  @JsonKey(ignore: true)
-  _$OpenPageCopyWith<_OpenPage> get copyWith =>
-      throw _privateConstructorUsedError;
-}
-
-/// @nodoc
-abstract class _$CreateAppCopyWith<$Res> {
-  factory _$CreateAppCopyWith(
-          _CreateApp value, $Res Function(_CreateApp) then) =
-      __$CreateAppCopyWithImpl<$Res>;
-}
-
-/// @nodoc
-class __$CreateAppCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
-    implements _$CreateAppCopyWith<$Res> {
-  __$CreateAppCopyWithImpl(_CreateApp _value, $Res Function(_CreateApp) _then)
-      : super(_value, (v) => _then(v as _CreateApp));
-
-  @override
-  _CreateApp get _value => super._value as _CreateApp;
-}
-
-/// @nodoc
-
-class _$_CreateApp implements _CreateApp {
-  const _$_CreateApp();
-
-  @override
-  String toString() {
-    return 'MenuEvent.createApp()';
-  }
-
-  @override
-  bool operator ==(dynamic other) {
-    return identical(this, other) || (other is _CreateApp);
-  }
-
-  @override
-  int get hashCode => runtimeType.hashCode;
-
-  @override
-  @optionalTypeArgs
-  TResult when<TResult extends Object?>({
-    required TResult Function() collapse,
-    required TResult Function(PageContext context) openPage,
-    required TResult Function() createApp,
-  }) {
-    return createApp();
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeWhen<TResult extends Object?>({
-    TResult Function()? collapse,
-    TResult Function(PageContext context)? openPage,
-    TResult Function()? createApp,
-    required TResult orElse(),
-  }) {
-    if (createApp != null) {
-      return createApp();
-    }
-    return orElse();
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult map<TResult extends Object?>({
-    required TResult Function(Collapse value) collapse,
-    required TResult Function(_OpenPage value) openPage,
-    required TResult Function(_CreateApp value) createApp,
-  }) {
-    return createApp(this);
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeMap<TResult extends Object?>({
-    TResult Function(Collapse value)? collapse,
-    TResult Function(_OpenPage value)? openPage,
-    TResult Function(_CreateApp value)? createApp,
-    required TResult orElse(),
-  }) {
-    if (createApp != null) {
-      return createApp(this);
-    }
-    return orElse();
-  }
-}
-
-abstract class _CreateApp implements MenuEvent {
-  const factory _CreateApp() = _$_CreateApp;
-}
-
-/// @nodoc
-class _$MenuStateTearOff {
-  const _$MenuStateTearOff();
-
-  _MenuState call(
-      {required bool isCollapse, required Option<PageContext> pageContext}) {
-    return _MenuState(
-      isCollapse: isCollapse,
-      pageContext: pageContext,
-    );
-  }
-}
-
-/// @nodoc
-const $MenuState = _$MenuStateTearOff();
-
-/// @nodoc
-mixin _$MenuState {
-  bool get isCollapse => throw _privateConstructorUsedError;
-  Option<PageContext> get pageContext => throw _privateConstructorUsedError;
-
-  @JsonKey(ignore: true)
-  $MenuStateCopyWith<MenuState> get copyWith =>
-      throw _privateConstructorUsedError;
-}
-
-/// @nodoc
-abstract class $MenuStateCopyWith<$Res> {
-  factory $MenuStateCopyWith(MenuState value, $Res Function(MenuState) then) =
-      _$MenuStateCopyWithImpl<$Res>;
-  $Res call({bool isCollapse, Option<PageContext> pageContext});
-}
-
-/// @nodoc
-class _$MenuStateCopyWithImpl<$Res> implements $MenuStateCopyWith<$Res> {
-  _$MenuStateCopyWithImpl(this._value, this._then);
-
-  final MenuState _value;
-  // ignore: unused_field
-  final $Res Function(MenuState) _then;
-
-  @override
-  $Res call({
-    Object? isCollapse = freezed,
-    Object? pageContext = freezed,
-  }) {
-    return _then(_value.copyWith(
-      isCollapse: isCollapse == freezed
-          ? _value.isCollapse
-          : isCollapse // ignore: cast_nullable_to_non_nullable
-              as bool,
-      pageContext: pageContext == freezed
-          ? _value.pageContext
-          : pageContext // ignore: cast_nullable_to_non_nullable
-              as Option<PageContext>,
-    ));
-  }
-}
-
-/// @nodoc
-abstract class _$MenuStateCopyWith<$Res> implements $MenuStateCopyWith<$Res> {
-  factory _$MenuStateCopyWith(
-          _MenuState value, $Res Function(_MenuState) then) =
-      __$MenuStateCopyWithImpl<$Res>;
-  @override
-  $Res call({bool isCollapse, Option<PageContext> pageContext});
-}
-
-/// @nodoc
-class __$MenuStateCopyWithImpl<$Res> extends _$MenuStateCopyWithImpl<$Res>
-    implements _$MenuStateCopyWith<$Res> {
-  __$MenuStateCopyWithImpl(_MenuState _value, $Res Function(_MenuState) _then)
-      : super(_value, (v) => _then(v as _MenuState));
-
-  @override
-  _MenuState get _value => super._value as _MenuState;
-
-  @override
-  $Res call({
-    Object? isCollapse = freezed,
-    Object? pageContext = freezed,
-  }) {
-    return _then(_MenuState(
-      isCollapse: isCollapse == freezed
-          ? _value.isCollapse
-          : isCollapse // ignore: cast_nullable_to_non_nullable
-              as bool,
-      pageContext: pageContext == freezed
-          ? _value.pageContext
-          : pageContext // ignore: cast_nullable_to_non_nullable
-              as Option<PageContext>,
-    ));
-  }
-}
-
-/// @nodoc
-
-class _$_MenuState implements _MenuState {
-  const _$_MenuState({required this.isCollapse, required this.pageContext});
-
-  @override
-  final bool isCollapse;
-  @override
-  final Option<PageContext> pageContext;
-
-  @override
-  String toString() {
-    return 'MenuState(isCollapse: $isCollapse, pageContext: $pageContext)';
-  }
-
-  @override
-  bool operator ==(dynamic other) {
-    return identical(this, other) ||
-        (other is _MenuState &&
-            (identical(other.isCollapse, isCollapse) ||
-                const DeepCollectionEquality()
-                    .equals(other.isCollapse, isCollapse)) &&
-            (identical(other.pageContext, pageContext) ||
-                const DeepCollectionEquality()
-                    .equals(other.pageContext, pageContext)));
-  }
-
-  @override
-  int get hashCode =>
-      runtimeType.hashCode ^
-      const DeepCollectionEquality().hash(isCollapse) ^
-      const DeepCollectionEquality().hash(pageContext);
-
-  @JsonKey(ignore: true)
-  @override
-  _$MenuStateCopyWith<_MenuState> get copyWith =>
-      __$MenuStateCopyWithImpl<_MenuState>(this, _$identity);
-}
-
-abstract class _MenuState implements MenuState {
-  const factory _MenuState(
-      {required bool isCollapse,
-      required Option<PageContext> pageContext}) = _$_MenuState;
-
-  @override
-  bool get isCollapse => throw _privateConstructorUsedError;
-  @override
-  Option<PageContext> get pageContext => throw _privateConstructorUsedError;
-  @override
-  @JsonKey(ignore: true)
-  _$MenuStateCopyWith<_MenuState> get copyWith =>
-      throw _privateConstructorUsedError;
-}

+ 0 - 8
app_flowy/lib/home/application/menu/menu_event.dart

@@ -1,8 +0,0 @@
-part of 'menu_bloc.dart';
-
-@freezed
-abstract class MenuEvent with _$MenuEvent {
-  const factory MenuEvent.collapse() = Collapse;
-  const factory MenuEvent.openPage(PageContext context) = _OpenPage;
-  const factory MenuEvent.createApp() = _CreateApp;
-}

+ 0 - 14
app_flowy/lib/home/application/menu/menu_state.dart

@@ -1,14 +0,0 @@
-part of 'menu_bloc.dart';
-
-@freezed
-abstract class MenuState implements _$MenuState {
-  const factory MenuState({
-    required bool isCollapse,
-    required Option<PageContext> pageContext,
-  }) = _MenuState;
-
-  factory MenuState.initial() => MenuState(
-        isCollapse: false,
-        pageContext: none(),
-      );
-}

+ 0 - 7
app_flowy/lib/home/application/watcher/home_watcher_event.dart

@@ -1,7 +0,0 @@
-part of 'home_watcher_bloc.dart';
-
-@freezed
-abstract class HomeWatcherEvent with _$HomeWatcherEvent {
-  const factory HomeWatcherEvent.started(String workspaceId) = _Started;
-  const factory HomeWatcherEvent.stop(String workspaceId) = _Stop;
-}

+ 0 - 7
app_flowy/lib/home/application/watcher/home_watcher_state.dart

@@ -1,7 +0,0 @@
-part of 'home_watcher_bloc.dart';
-
-@freezed
-abstract class HomeWatcherState with _$HomeWatcherState {
-  const factory HomeWatcherState.initial() = _Initial;
-  const factory HomeWatcherState.loading() = _Loading;
-}

+ 0 - 19
app_flowy/lib/home/domain/page_context.dart

@@ -1,19 +0,0 @@
-import 'package:equatable/equatable.dart';
-import 'package:flutter/material.dart';
-
-abstract class HomeStackPage extends StatefulWidget {
-  final PageContext pageContext;
-  const HomeStackPage({Key? key, required this.pageContext}) : super(key: key);
-}
-
-enum PageType {
-  blank,
-}
-
-List<PageType> pages = PageType.values.toList();
-
-abstract class PageContext extends Equatable {
-  final PageType pageType;
-  final String pageTitle;
-  const PageContext(this.pageType, {required this.pageTitle});
-}

+ 0 - 121
app_flowy/lib/home/presentation/widgets/menu/home_menu.dart

@@ -1,121 +0,0 @@
-import 'package:app_flowy/home/application/menu/menu_bloc.dart';
-import 'package:app_flowy/home/domain/page_context.dart';
-import 'package:app_flowy/startup/startup.dart';
-import 'package:dartz/dartz.dart';
-import 'package:flowy_infra/size.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import '../../home_sizes.dart';
-import 'package:styled_widget/styled_widget.dart';
-
-class HomeMenu extends StatelessWidget {
-  final Function(Option<PageContext>) pageContextChanged;
-  final Function(bool) isCollapseChanged;
-
-  const HomeMenu(
-      {Key? key,
-      required this.pageContextChanged,
-      required this.isCollapseChanged})
-      : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    return MultiBlocProvider(
-        providers: [
-          BlocProvider<MenuBloc>(create: (context) => getIt<MenuBloc>()),
-        ],
-        child: MultiBlocListener(
-          listeners: bind(),
-          child: Container(
-            color: Theme.of(context).colorScheme.primaryVariant,
-            child: Padding(
-              padding: EdgeInsets.symmetric(horizontal: Insets.sm),
-              child: Column(
-                mainAxisAlignment: MainAxisAlignment.start,
-                children: [
-                  const MenuTopBar(),
-                  Container(),
-                  const NewAppButton(),
-                ],
-              ),
-            ),
-          ),
-        ));
-  }
-
-  // bind the function passed by ooutter with the bloc listener
-  List<BlocListener<MenuBloc, MenuState>> bind() {
-    return [
-      BlocListener<MenuBloc, MenuState>(
-        listenWhen: (p, c) => p.pageContext != c.pageContext,
-        listener: (context, state) => pageContextChanged(state.pageContext),
-      ),
-      BlocListener<MenuBloc, MenuState>(
-        listenWhen: (p, c) => p.isCollapse != c.isCollapse,
-        listener: (context, state) => isCollapseChanged(state.isCollapse),
-      )
-    ];
-  }
-}
-
-class MenuTopBar extends StatelessWidget {
-  const MenuTopBar({Key? key}) : super(key: key);
-  @override
-  Widget build(BuildContext context) {
-    return BlocBuilder<MenuBloc, MenuState>(
-      builder: (context, state) {
-        return SizedBox(
-          height: HomeSizes.menuTopBarHeight,
-          child: Row(
-            children: [
-              const Text(
-                'AppFlowy',
-                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
-              ).constrained(minWidth: 100),
-              const Spacer(),
-              IconButton(
-                icon: const Icon(Icons.arrow_left),
-                onPressed: () =>
-                    context.read<MenuBloc>().add(const MenuEvent.collapse()),
-              ),
-            ],
-          ),
-        );
-      },
-    );
-  }
-}
-
-class NewAppButton extends StatelessWidget {
-  const NewAppButton({Key? key}) : super(key: key);
-  @override
-  Widget build(BuildContext context) {
-    return SizedBox(
-      height: HomeSizes.menuAddButtonHeight,
-      child: Row(
-        mainAxisAlignment: MainAxisAlignment.start,
-        children: [
-          const Icon(Icons.add),
-          const SizedBox(
-            width: 10,
-          ),
-          TextButton(
-            onPressed: () async {
-              // Dialogs.show(OkCancelDialog(
-              //   title: "No Connection",
-              //   message:
-              //       "It appears your device is offline. Please check your connection and try again.",
-              //   onOkPressed: () => AppGlobals.nav.pop(),
-              // ));
-            },
-            child: const Text('New App',
-                style: TextStyle(
-                    color: Colors.black,
-                    fontWeight: FontWeight.bold,
-                    fontSize: 20)),
-          )
-        ],
-      ),
-    );
-  }
-}

+ 0 - 2
app_flowy/lib/home/presentation/widgets/menu/prelude.dart

@@ -1,2 +0,0 @@
-export 'home_menu.dart';
-export 'hom_menu_size.dart';

+ 1 - 1
app_flowy/lib/main.dart

@@ -10,5 +10,5 @@ class FlowyAppFactory implements AppFactory {
 }
 
 void main() {
-  App.run(FlowyAppFactory());
+  Application.run(FlowyAppFactory());
 }

+ 4 - 2
app_flowy/lib/startup/deps_inject/prelude.dart

@@ -1,7 +1,8 @@
+import 'package:app_flowy/workspace/infrastructure/deps_resolver.dart';
 import 'package:app_flowy/startup/launch.dart';
 import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/user/infrastructure/interface_impl.dart';
-import 'package:app_flowy/welcome/infrastructure/interface_impl.dart';
+import 'package:app_flowy/user/infrastructure/deps_resolver.dart';
+import 'package:app_flowy/welcome/infrastructure/deps_resolver.dart';
 import 'package:flowy_sdk/flowy_sdk.dart';
 import 'package:get_it/get_it.dart';
 
@@ -16,4 +17,5 @@ Future<void> initGetIt(
 
   await WelcomeDepsResolver.resolve(getIt);
   await UserDepsResolver.resolve(getIt);
+  await HomeDepsResolver.resolve(getIt);
 }

+ 1 - 1
app_flowy/lib/startup/startup.dart

@@ -14,7 +14,7 @@ abstract class AppFactory {
   Widget create();
 }
 
-class App {
+class Application {
   static void run(AppFactory f) {
     // Specify the evn
     const env = IntegrationEnv.dev;

+ 3 - 3
app_flowy/lib/startup/tasks/app_widget_task.dart → app_flowy/lib/startup/tasks/application_task.dart

@@ -12,16 +12,16 @@ class AppWidgetTask extends LaunchTask {
   @override
   Future<void> initialize(LaunchContext context) {
     final widget = context.getIt<AppFactory>().create();
-    final app = AppWidget(child: widget);
+    final app = ApplicationWidget(child: widget);
     runApp(app);
 
     return Future(() => {});
   }
 }
 
-class AppWidget extends StatelessWidget {
+class ApplicationWidget extends StatelessWidget {
   final Widget child;
-  const AppWidget({
+  const ApplicationWidget({
     Key? key,
     required this.child,
   }) : super(key: key);

+ 1 - 1
app_flowy/lib/startup/tasks/prelude.dart

@@ -1,2 +1,2 @@
-export 'app_widget_task.dart';
+export 'application_task.dart';
 export 'rust_sdk_init_task.dart';

+ 2 - 1
app_flowy/lib/startup/tasks/rust_sdk_init_task.dart

@@ -38,8 +38,9 @@ class RustSDKInitTask extends LaunchTask {
 
 class ApplicationBlocObserver extends BlocObserver {
   @override
+  // ignore: unnecessary_overrides
   void onTransition(Bloc bloc, Transition transition) {
-    Log.debug(transition);
+    // Log.debug(transition);
     super.onTransition(bloc, transition);
   }
 

+ 25 - 3
app_flowy/lib/user/application/sign_in/sign_in_bloc.dart

@@ -1,12 +1,10 @@
-import 'package:app_flowy/user/domain/interface.dart';
+import 'package:app_flowy/user/domain/i_auth.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 // ignore: import_of_legacy_library_into_null_safe
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-part 'sign_in_event.dart';
-part 'sign_in_state.dart';
 part 'sign_in_bloc.freezed.dart';
 
 class SignInBloc extends Bloc<SignInEvent, SignInState> {
@@ -43,3 +41,27 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
     );
   }
 }
+
+@freezed
+abstract class SignInEvent with _$SignInEvent {
+  const factory SignInEvent.signedInWithUserEmailAndPassword() =
+      SignedInWithUserEmailAndPassword;
+
+  const factory SignInEvent.emailChanged(String email) = EmailChanged;
+  const factory SignInEvent.passwordChanged(String password) = PasswordChanged;
+}
+
+@freezed
+abstract class SignInState with _$SignInState {
+  const factory SignInState({
+    String? email,
+    String? password,
+    required bool isSubmitting,
+    required Option<Either<UserDetail, UserError>> signInFailure,
+  }) = _SignInState;
+
+  factory SignInState.initial() => SignInState(
+        isSubmitting: false,
+        signInFailure: none(),
+      );
+}

+ 0 - 10
app_flowy/lib/user/application/sign_in/sign_in_event.dart

@@ -1,10 +0,0 @@
-part of 'sign_in_bloc.dart';
-
-@freezed
-abstract class SignInEvent with _$SignInEvent {
-  const factory SignInEvent.signedInWithUserEmailAndPassword() =
-      SignedInWithUserEmailAndPassword;
-
-  const factory SignInEvent.emailChanged(String email) = EmailChanged;
-  const factory SignInEvent.passwordChanged(String password) = PasswordChanged;
-}

+ 0 - 16
app_flowy/lib/user/application/sign_in/sign_in_state.dart

@@ -1,16 +0,0 @@
-part of 'sign_in_bloc.dart';
-
-@freezed
-abstract class SignInState with _$SignInState {
-  const factory SignInState({
-    String? email,
-    String? password,
-    required bool isSubmitting,
-    required Option<Either<UserDetail, UserError>> signInFailure,
-  }) = _SignInState;
-
-  factory SignInState.initial() => SignInState(
-        isSubmitting: false,
-        signInFailure: none(),
-      );
-}

+ 0 - 0
app_flowy/lib/user/domain/interface.dart → app_flowy/lib/user/domain/i_auth.dart


+ 17 - 0
app_flowy/lib/user/infrastructure/deps_resolver.dart

@@ -0,0 +1,17 @@
+import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart';
+import 'package:app_flowy/user/domain/i_auth.dart';
+import 'package:app_flowy/user/infrastructure/repos/auth_repo.dart';
+import 'package:app_flowy/user/infrastructure/i_auth_impl.dart';
+import 'package:get_it/get_it.dart';
+
+class UserDepsResolver {
+  static Future<void> resolve(GetIt getIt) async {
+    getIt.registerLazySingleton<AuthRepository>(() => AuthRepository());
+
+    //Interface implementation
+    getIt.registerFactory<IAuth>(() => AuthImpl(repo: getIt<AuthRepository>()));
+
+    //Bloc
+    getIt.registerFactory<SignInBloc>(() => SignInBloc(getIt<IAuth>()));
+  }
+}

+ 2 - 17
app_flowy/lib/user/infrastructure/interface_impl.dart → app_flowy/lib/user/infrastructure/i_auth_impl.dart

@@ -1,22 +1,7 @@
-import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
-import 'package:get_it/get_it.dart';
-
-import 'package:app_flowy/user/domain/interface.dart';
-import 'package:app_flowy/user/infrastructure/auth_repo.dart';
-
-class UserDepsResolver {
-  static Future<void> resolve(GetIt getIt) async {
-    getIt.registerLazySingleton<AuthRepository>(() => AuthRepository());
-
-    //Interface implementation
-    getIt.registerFactory<IAuth>(() => AuthImpl(repo: getIt<AuthRepository>()));
-
-    //Bloc
-    getIt.registerFactory<SignInBloc>(() => SignInBloc(getIt<IAuth>()));
-  }
-}
+import 'package:app_flowy/user/domain/i_auth.dart';
+import 'package:app_flowy/user/infrastructure/repos/auth_repo.dart';
 
 class AuthImpl extends IAuth {
   AuthRepository repo;

+ 0 - 0
app_flowy/lib/user/infrastructure/auth_repo.dart → app_flowy/lib/user/infrastructure/repos/auth_repo.dart


+ 1 - 1
app_flowy/lib/user/presentation/sign_in/widgets/body.dart

@@ -1,7 +1,7 @@
-import 'package:app_flowy/home/presentation/home_screen.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart';
 import 'package:app_flowy/user/presentation/sign_in/widgets/background.dart';
+import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_infra_ui/widget/rounded_button.dart';
 import 'package:flowy_infra_ui/widget/rounded_input_field.dart';

+ 17 - 3
app_flowy/lib/welcome/application/welcome_bloc.dart

@@ -1,10 +1,8 @@
 import 'package:app_flowy/welcome/domain/auth_state.dart';
-import 'package:app_flowy/welcome/domain/interface.dart';
+import 'package:app_flowy/welcome/domain/i_welcome.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 
-part 'welcome_event.dart';
-part 'welcome_state.dart';
 part 'welcome_bloc.freezed.dart';
 
 class WelcomeBloc extends Bloc<WelcomeEvent, WelcomeState> {
@@ -21,3 +19,19 @@ class WelcomeBloc extends Bloc<WelcomeEvent, WelcomeState> {
     );
   }
 }
+
+@freezed
+abstract class WelcomeEvent with _$WelcomeEvent {
+  const factory WelcomeEvent.getUser() = _GetUser;
+}
+
+@freezed
+abstract class WelcomeState implements _$WelcomeState {
+  const factory WelcomeState({
+    required AuthState auth,
+  }) = _WelcomeState;
+
+  factory WelcomeState.initial() => const WelcomeState(
+        auth: AuthState.initial(),
+      );
+}

+ 0 - 6
app_flowy/lib/welcome/application/welcome_event.dart

@@ -1,6 +0,0 @@
-part of 'welcome_bloc.dart';
-
-@freezed
-abstract class WelcomeEvent with _$WelcomeEvent {
-  const factory WelcomeEvent.getUser() = _GetUser;
-}

+ 0 - 12
app_flowy/lib/welcome/application/welcome_state.dart

@@ -1,12 +0,0 @@
-part of 'welcome_bloc.dart';
-
-@freezed
-abstract class WelcomeState implements _$WelcomeState {
-  const factory WelcomeState({
-    required AuthState auth,
-  }) = _WelcomeState;
-
-  factory WelcomeState.initial() => const WelcomeState(
-        auth: AuthState.initial(),
-      );
-}

+ 0 - 0
app_flowy/lib/welcome/domain/interface.dart → app_flowy/lib/welcome/domain/i_welcome.dart


+ 19 - 0
app_flowy/lib/welcome/infrastructure/deps_resolver.dart

@@ -0,0 +1,19 @@
+import 'package:app_flowy/workspace/application/edit_pannel/edit_pannel_bloc.dart';
+import 'package:app_flowy/welcome/application/welcome_bloc.dart';
+import 'package:app_flowy/welcome/infrastructure/i_welcome_impl.dart';
+import 'package:app_flowy/workspace/application/home/home_bloc.dart';
+import 'package:app_flowy/workspace/application/home/home_watcher_bloc.dart';
+import 'package:get_it/get_it.dart';
+
+class WelcomeDepsResolver {
+  static Future<void> resolve(GetIt getIt) async {
+    getIt.registerFactory<IWelcomeAuth>(() => WelcomeAuthImpl());
+    getIt.registerFactory<IWelcomeRoute>(() => WelcomeRoute());
+    getIt.registerFactory<HomeBloc>(() => HomeBloc());
+    getIt.registerFactory<HomeWatcherBloc>(() => HomeWatcherBloc());
+    getIt.registerFactory<EditPannelBloc>(() => EditPannelBloc());
+
+    getIt
+        .registerFactory<WelcomeBloc>(() => WelcomeBloc(getIt<IWelcomeAuth>()));
+  }
+}

+ 39 - 0
app_flowy/lib/welcome/infrastructure/i_welcome_impl.dart

@@ -0,0 +1,39 @@
+import 'package:app_flowy/user/presentation/sign_in/sign_in_screen.dart';
+import 'package:app_flowy/welcome/domain/auth_state.dart';
+import 'package:app_flowy/welcome/domain/i_welcome.dart';
+import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
+import 'package:flowy_sdk/dispatch/dispatch.dart';
+import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+
+export 'package:app_flowy/welcome/domain/i_welcome.dart';
+
+class WelcomeAuthImpl implements IWelcomeAuth {
+  @override
+  Future<AuthState> currentUserState() {
+    final result = UserEventGetStatus().send();
+    return result.then((result) {
+      return result.fold(
+        (userDetail) {
+          return AuthState.authenticated(userDetail);
+        },
+        (userError) {
+          return AuthState.unauthenticated(userError);
+        },
+      );
+    });
+  }
+}
+
+class WelcomeRoute implements IWelcomeRoute {
+  @override
+  Widget pushHomeScreen(UserDetail user) {
+    return HomeScreen(user);
+  }
+
+  @override
+  Widget pushSignInScreen() {
+    return const SignInScreen();
+  }
+}

+ 0 - 58
app_flowy/lib/welcome/infrastructure/interface_impl.dart

@@ -1,58 +0,0 @@
-import 'package:app_flowy/home/application/edit_pannel/edit_pannel_bloc.dart';
-import 'package:app_flowy/home/application/home_bloc.dart';
-import 'package:app_flowy/home/application/menu/menu_bloc.dart';
-import 'package:app_flowy/home/application/watcher/home_watcher_bloc.dart';
-import 'package:app_flowy/home/presentation/home_screen.dart';
-import 'package:app_flowy/user/presentation/sign_in/sign_in_screen.dart';
-import 'package:app_flowy/welcome/application/welcome_bloc.dart';
-import 'package:app_flowy/welcome/domain/auth_state.dart';
-import 'package:app_flowy/welcome/domain/interface.dart';
-import 'package:flowy_sdk/dispatch/dispatch.dart';
-import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
-import 'package:get_it/get_it.dart';
-
-class WelcomeDepsResolver {
-  static Future<void> resolve(GetIt getIt) async {
-    getIt.registerFactory<IWelcomeAuth>(() => WelcomeAuthImpl());
-    getIt.registerFactory<IWelcomeRoute>(() => WelcomeRoute());
-    getIt.registerFactory<HomeBloc>(() => HomeBloc());
-    getIt.registerFactory<HomeWatcherBloc>(() => HomeWatcherBloc());
-    getIt.registerFactory<EditPannelBloc>(() => EditPannelBloc());
-
-    getIt.registerFactory<MenuBloc>(() => MenuBloc());
-
-    getIt
-        .registerFactory<WelcomeBloc>(() => WelcomeBloc(getIt<IWelcomeAuth>()));
-  }
-}
-
-class WelcomeAuthImpl implements IWelcomeAuth {
-  @override
-  Future<AuthState> currentUserState() {
-    final result = UserEventGetStatus().send();
-    return result.then((result) {
-      return result.fold(
-        (userDetail) {
-          return AuthState.authenticated(userDetail);
-        },
-        (userError) {
-          return AuthState.unauthenticated(userError);
-        },
-      );
-    });
-  }
-}
-
-class WelcomeRoute implements IWelcomeRoute {
-  @override
-  Widget pushHomeScreen(UserDetail user) {
-    return HomeScreen(user);
-  }
-
-  @override
-  Widget pushSignInScreen() {
-    return const SignInScreen();
-  }
-}

+ 1 - 1
app_flowy/lib/welcome/presentation/welcome_screen.dart

@@ -1,4 +1,4 @@
-import 'package:app_flowy/welcome/domain/interface.dart';
+import 'package:app_flowy/welcome/domain/i_welcome.dart';
 import 'package:app_flowy/welcome/domain/auth_state.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/welcome/application/welcome_bloc.dart';

+ 58 - 0
app_flowy/lib/workspace/application/app/app_bloc.dart

@@ -0,0 +1,58 @@
+import 'package:app_flowy/workspace/domain/i_app.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:dartz/dartz.dart';
+
+part 'app_bloc.freezed.dart';
+
+class AppBloc extends Bloc<AppEvent, AppState> {
+  final IApp iAppImpl;
+  AppBloc(this.iAppImpl) : super(AppState.initial());
+
+  @override
+  Stream<AppState> mapEventToState(
+    AppEvent event,
+  ) async* {
+    yield* event.map(
+      initial: (e) async* {
+        yield* _fetchViews();
+      },
+      createView: (CreateView value) async* {
+        iAppImpl.createView(
+            name: value.name, desc: value.desc, viewType: value.viewType);
+      },
+    );
+  }
+
+  Stream<AppState> _fetchViews() async* {
+    final viewsOrFailed = await iAppImpl.getViews();
+    yield viewsOrFailed.fold(
+      (apps) => state.copyWith(views: some(apps)),
+      (error) => state.copyWith(successOrFailure: right(error)),
+    );
+  }
+}
+
+@freezed
+abstract class AppEvent with _$AppEvent {
+  const factory AppEvent.initial() = Initial;
+  const factory AppEvent.createView(
+      String name, String desc, ViewType viewType) = CreateView;
+}
+
+@freezed
+abstract class AppState implements _$AppState {
+  const factory AppState({
+    required bool isLoading,
+    required Option<List<View>> views,
+    required Either<Unit, WorkspaceError> successOrFailure,
+  }) = _AppState;
+
+  factory AppState.initial() => AppState(
+        isLoading: false,
+        views: none(),
+        successOrFailure: left(unit),
+      );
+}

+ 484 - 0
app_flowy/lib/workspace/application/app/app_bloc.freezed.dart

@@ -0,0 +1,484 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
+
+part of 'app_bloc.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+class _$AppEventTearOff {
+  const _$AppEventTearOff();
+
+  Initial initial() {
+    return const Initial();
+  }
+
+  CreateView createView(String name, String desc, ViewType viewType) {
+    return CreateView(
+      name,
+      desc,
+      viewType,
+    );
+  }
+}
+
+/// @nodoc
+const $AppEvent = _$AppEventTearOff();
+
+/// @nodoc
+mixin _$AppEvent {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(String name, String desc, ViewType viewType)
+        createView,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(String name, String desc, ViewType viewType)? createView,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+    required TResult Function(CreateView value) createView,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(CreateView value)? createView,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $AppEventCopyWith<$Res> {
+  factory $AppEventCopyWith(AppEvent value, $Res Function(AppEvent) then) =
+      _$AppEventCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$AppEventCopyWithImpl<$Res> implements $AppEventCopyWith<$Res> {
+  _$AppEventCopyWithImpl(this._value, this._then);
+
+  final AppEvent _value;
+  // ignore: unused_field
+  final $Res Function(AppEvent) _then;
+}
+
+/// @nodoc
+abstract class $InitialCopyWith<$Res> {
+  factory $InitialCopyWith(Initial value, $Res Function(Initial) then) =
+      _$InitialCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$InitialCopyWithImpl<$Res> extends _$AppEventCopyWithImpl<$Res>
+    implements $InitialCopyWith<$Res> {
+  _$InitialCopyWithImpl(Initial _value, $Res Function(Initial) _then)
+      : super(_value, (v) => _then(v as Initial));
+
+  @override
+  Initial get _value => super._value as Initial;
+}
+
+/// @nodoc
+
+class _$Initial implements Initial {
+  const _$Initial();
+
+  @override
+  String toString() {
+    return 'AppEvent.initial()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is Initial);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(String name, String desc, ViewType viewType)
+        createView,
+  }) {
+    return initial();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(String name, String desc, ViewType viewType)? createView,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+    required TResult Function(CreateView value) createView,
+  }) {
+    return initial(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(CreateView value)? createView,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class Initial implements AppEvent {
+  const factory Initial() = _$Initial;
+}
+
+/// @nodoc
+abstract class $CreateViewCopyWith<$Res> {
+  factory $CreateViewCopyWith(
+          CreateView value, $Res Function(CreateView) then) =
+      _$CreateViewCopyWithImpl<$Res>;
+  $Res call({String name, String desc, ViewType viewType});
+}
+
+/// @nodoc
+class _$CreateViewCopyWithImpl<$Res> extends _$AppEventCopyWithImpl<$Res>
+    implements $CreateViewCopyWith<$Res> {
+  _$CreateViewCopyWithImpl(CreateView _value, $Res Function(CreateView) _then)
+      : super(_value, (v) => _then(v as CreateView));
+
+  @override
+  CreateView get _value => super._value as CreateView;
+
+  @override
+  $Res call({
+    Object? name = freezed,
+    Object? desc = freezed,
+    Object? viewType = freezed,
+  }) {
+    return _then(CreateView(
+      name == freezed
+          ? _value.name
+          : name // ignore: cast_nullable_to_non_nullable
+              as String,
+      desc == freezed
+          ? _value.desc
+          : desc // ignore: cast_nullable_to_non_nullable
+              as String,
+      viewType == freezed
+          ? _value.viewType
+          : viewType // ignore: cast_nullable_to_non_nullable
+              as ViewType,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$CreateView implements CreateView {
+  const _$CreateView(this.name, this.desc, this.viewType);
+
+  @override
+  final String name;
+  @override
+  final String desc;
+  @override
+  final ViewType viewType;
+
+  @override
+  String toString() {
+    return 'AppEvent.createView(name: $name, desc: $desc, viewType: $viewType)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is CreateView &&
+            (identical(other.name, name) ||
+                const DeepCollectionEquality().equals(other.name, name)) &&
+            (identical(other.desc, desc) ||
+                const DeepCollectionEquality().equals(other.desc, desc)) &&
+            (identical(other.viewType, viewType) ||
+                const DeepCollectionEquality()
+                    .equals(other.viewType, viewType)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^
+      const DeepCollectionEquality().hash(name) ^
+      const DeepCollectionEquality().hash(desc) ^
+      const DeepCollectionEquality().hash(viewType);
+
+  @JsonKey(ignore: true)
+  @override
+  $CreateViewCopyWith<CreateView> get copyWith =>
+      _$CreateViewCopyWithImpl<CreateView>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(String name, String desc, ViewType viewType)
+        createView,
+  }) {
+    return createView(name, desc, viewType);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(String name, String desc, ViewType viewType)? createView,
+    required TResult orElse(),
+  }) {
+    if (createView != null) {
+      return createView(name, desc, viewType);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+    required TResult Function(CreateView value) createView,
+  }) {
+    return createView(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(CreateView value)? createView,
+    required TResult orElse(),
+  }) {
+    if (createView != null) {
+      return createView(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class CreateView implements AppEvent {
+  const factory CreateView(String name, String desc, ViewType viewType) =
+      _$CreateView;
+
+  String get name => throw _privateConstructorUsedError;
+  String get desc => throw _privateConstructorUsedError;
+  ViewType get viewType => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $CreateViewCopyWith<CreateView> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+class _$AppStateTearOff {
+  const _$AppStateTearOff();
+
+  _AppState call(
+      {required bool isLoading,
+      required Option<List<View>> views,
+      required Either<Unit, WorkspaceError> successOrFailure}) {
+    return _AppState(
+      isLoading: isLoading,
+      views: views,
+      successOrFailure: successOrFailure,
+    );
+  }
+}
+
+/// @nodoc
+const $AppState = _$AppStateTearOff();
+
+/// @nodoc
+mixin _$AppState {
+  bool get isLoading => throw _privateConstructorUsedError;
+  Option<List<View>> get views => throw _privateConstructorUsedError;
+  Either<Unit, WorkspaceError> get successOrFailure =>
+      throw _privateConstructorUsedError;
+
+  @JsonKey(ignore: true)
+  $AppStateCopyWith<AppState> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $AppStateCopyWith<$Res> {
+  factory $AppStateCopyWith(AppState value, $Res Function(AppState) then) =
+      _$AppStateCopyWithImpl<$Res>;
+  $Res call(
+      {bool isLoading,
+      Option<List<View>> views,
+      Either<Unit, WorkspaceError> successOrFailure});
+}
+
+/// @nodoc
+class _$AppStateCopyWithImpl<$Res> implements $AppStateCopyWith<$Res> {
+  _$AppStateCopyWithImpl(this._value, this._then);
+
+  final AppState _value;
+  // ignore: unused_field
+  final $Res Function(AppState) _then;
+
+  @override
+  $Res call({
+    Object? isLoading = freezed,
+    Object? views = freezed,
+    Object? successOrFailure = freezed,
+  }) {
+    return _then(_value.copyWith(
+      isLoading: isLoading == freezed
+          ? _value.isLoading
+          : isLoading // ignore: cast_nullable_to_non_nullable
+              as bool,
+      views: views == freezed
+          ? _value.views
+          : views // ignore: cast_nullable_to_non_nullable
+              as Option<List<View>>,
+      successOrFailure: successOrFailure == freezed
+          ? _value.successOrFailure
+          : successOrFailure // ignore: cast_nullable_to_non_nullable
+              as Either<Unit, WorkspaceError>,
+    ));
+  }
+}
+
+/// @nodoc
+abstract class _$AppStateCopyWith<$Res> implements $AppStateCopyWith<$Res> {
+  factory _$AppStateCopyWith(_AppState value, $Res Function(_AppState) then) =
+      __$AppStateCopyWithImpl<$Res>;
+  @override
+  $Res call(
+      {bool isLoading,
+      Option<List<View>> views,
+      Either<Unit, WorkspaceError> successOrFailure});
+}
+
+/// @nodoc
+class __$AppStateCopyWithImpl<$Res> extends _$AppStateCopyWithImpl<$Res>
+    implements _$AppStateCopyWith<$Res> {
+  __$AppStateCopyWithImpl(_AppState _value, $Res Function(_AppState) _then)
+      : super(_value, (v) => _then(v as _AppState));
+
+  @override
+  _AppState get _value => super._value as _AppState;
+
+  @override
+  $Res call({
+    Object? isLoading = freezed,
+    Object? views = freezed,
+    Object? successOrFailure = freezed,
+  }) {
+    return _then(_AppState(
+      isLoading: isLoading == freezed
+          ? _value.isLoading
+          : isLoading // ignore: cast_nullable_to_non_nullable
+              as bool,
+      views: views == freezed
+          ? _value.views
+          : views // ignore: cast_nullable_to_non_nullable
+              as Option<List<View>>,
+      successOrFailure: successOrFailure == freezed
+          ? _value.successOrFailure
+          : successOrFailure // ignore: cast_nullable_to_non_nullable
+              as Either<Unit, WorkspaceError>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_AppState implements _AppState {
+  const _$_AppState(
+      {required this.isLoading,
+      required this.views,
+      required this.successOrFailure});
+
+  @override
+  final bool isLoading;
+  @override
+  final Option<List<View>> views;
+  @override
+  final Either<Unit, WorkspaceError> successOrFailure;
+
+  @override
+  String toString() {
+    return 'AppState(isLoading: $isLoading, views: $views, successOrFailure: $successOrFailure)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is _AppState &&
+            (identical(other.isLoading, isLoading) ||
+                const DeepCollectionEquality()
+                    .equals(other.isLoading, isLoading)) &&
+            (identical(other.views, views) ||
+                const DeepCollectionEquality().equals(other.views, views)) &&
+            (identical(other.successOrFailure, successOrFailure) ||
+                const DeepCollectionEquality()
+                    .equals(other.successOrFailure, successOrFailure)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^
+      const DeepCollectionEquality().hash(isLoading) ^
+      const DeepCollectionEquality().hash(views) ^
+      const DeepCollectionEquality().hash(successOrFailure);
+
+  @JsonKey(ignore: true)
+  @override
+  _$AppStateCopyWith<_AppState> get copyWith =>
+      __$AppStateCopyWithImpl<_AppState>(this, _$identity);
+}
+
+abstract class _AppState implements AppState {
+  const factory _AppState(
+      {required bool isLoading,
+      required Option<List<View>> views,
+      required Either<Unit, WorkspaceError> successOrFailure}) = _$_AppState;
+
+  @override
+  bool get isLoading => throw _privateConstructorUsedError;
+  @override
+  Option<List<View>> get views => throw _privateConstructorUsedError;
+  @override
+  Either<Unit, WorkspaceError> get successOrFailure =>
+      throw _privateConstructorUsedError;
+  @override
+  @JsonKey(ignore: true)
+  _$AppStateCopyWith<_AppState> get copyWith =>
+      throw _privateConstructorUsedError;
+}

+ 56 - 0
app_flowy/lib/workspace/application/app/app_watch_bloc.dart

@@ -0,0 +1,56 @@
+import 'package:app_flowy/workspace/domain/i_app.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:dartz/dartz.dart';
+
+part 'app_watch_bloc.freezed.dart';
+
+class AppWatchBloc extends Bloc<AppWatchEvent, AppWatchState> {
+  final IAppWatch watcher;
+  AppWatchBloc(this.watcher) : super(const AppWatchState.initial());
+
+  @override
+  Stream<AppWatchState> mapEventToState(
+    AppWatchEvent event,
+  ) async* {
+    yield* event.map(started: (_) async* {
+      watcher.startWatching(
+        addViewCallback: (viewsOrFail) => _handleViewsOrFail(viewsOrFail),
+      );
+    }, viewsReceived: (ViewsReceived value) async* {
+      yield value.viewsOrFail.fold(
+        (views) => AppWatchState.loadViews(views),
+        (error) => AppWatchState.loadFail(error),
+      );
+    });
+  }
+
+  void _handleViewsOrFail(Either<List<View>, WorkspaceError> viewsOrFail) {
+    viewsOrFail.fold(
+      (views) => add(AppWatchEvent.viewsReceived(left(views))),
+      (error) => add(AppWatchEvent.viewsReceived(right(error))),
+    );
+  }
+}
+
+@freezed
+abstract class AppWatchEvent with _$AppWatchEvent {
+  const factory AppWatchEvent.started() = _Started;
+  const factory AppWatchEvent.viewsReceived(
+      Either<List<View>, WorkspaceError> viewsOrFail) = ViewsReceived;
+}
+
+@freezed
+abstract class AppWatchState implements _$AppWatchState {
+  const factory AppWatchState.initial() = _Initial;
+
+  const factory AppWatchState.loadViews(
+    List<View> views,
+  ) = _LoadViews;
+
+  const factory AppWatchState.loadFail(
+    WorkspaceError error,
+  ) = _LoadFail;
+}

+ 683 - 0
app_flowy/lib/workspace/application/app/app_watch_bloc.freezed.dart

@@ -0,0 +1,683 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
+
+part of 'app_watch_bloc.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+class _$AppWatchEventTearOff {
+  const _$AppWatchEventTearOff();
+
+  _Started started() {
+    return const _Started();
+  }
+
+  ViewsReceived viewsReceived(Either<List<View>, WorkspaceError> viewsOrFail) {
+    return ViewsReceived(
+      viewsOrFail,
+    );
+  }
+}
+
+/// @nodoc
+const $AppWatchEvent = _$AppWatchEventTearOff();
+
+/// @nodoc
+mixin _$AppWatchEvent {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() started,
+    required TResult Function(Either<List<View>, WorkspaceError> viewsOrFail)
+        viewsReceived,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? started,
+    TResult Function(Either<List<View>, WorkspaceError> viewsOrFail)?
+        viewsReceived,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Started value) started,
+    required TResult Function(ViewsReceived value) viewsReceived,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Started value)? started,
+    TResult Function(ViewsReceived value)? viewsReceived,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $AppWatchEventCopyWith<$Res> {
+  factory $AppWatchEventCopyWith(
+          AppWatchEvent value, $Res Function(AppWatchEvent) then) =
+      _$AppWatchEventCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$AppWatchEventCopyWithImpl<$Res>
+    implements $AppWatchEventCopyWith<$Res> {
+  _$AppWatchEventCopyWithImpl(this._value, this._then);
+
+  final AppWatchEvent _value;
+  // ignore: unused_field
+  final $Res Function(AppWatchEvent) _then;
+}
+
+/// @nodoc
+abstract class _$StartedCopyWith<$Res> {
+  factory _$StartedCopyWith(_Started value, $Res Function(_Started) then) =
+      __$StartedCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class __$StartedCopyWithImpl<$Res> extends _$AppWatchEventCopyWithImpl<$Res>
+    implements _$StartedCopyWith<$Res> {
+  __$StartedCopyWithImpl(_Started _value, $Res Function(_Started) _then)
+      : super(_value, (v) => _then(v as _Started));
+
+  @override
+  _Started get _value => super._value as _Started;
+}
+
+/// @nodoc
+
+class _$_Started implements _Started {
+  const _$_Started();
+
+  @override
+  String toString() {
+    return 'AppWatchEvent.started()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is _Started);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() started,
+    required TResult Function(Either<List<View>, WorkspaceError> viewsOrFail)
+        viewsReceived,
+  }) {
+    return started();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? started,
+    TResult Function(Either<List<View>, WorkspaceError> viewsOrFail)?
+        viewsReceived,
+    required TResult orElse(),
+  }) {
+    if (started != null) {
+      return started();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Started value) started,
+    required TResult Function(ViewsReceived value) viewsReceived,
+  }) {
+    return started(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Started value)? started,
+    TResult Function(ViewsReceived value)? viewsReceived,
+    required TResult orElse(),
+  }) {
+    if (started != null) {
+      return started(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _Started implements AppWatchEvent {
+  const factory _Started() = _$_Started;
+}
+
+/// @nodoc
+abstract class $ViewsReceivedCopyWith<$Res> {
+  factory $ViewsReceivedCopyWith(
+          ViewsReceived value, $Res Function(ViewsReceived) then) =
+      _$ViewsReceivedCopyWithImpl<$Res>;
+  $Res call({Either<List<View>, WorkspaceError> viewsOrFail});
+}
+
+/// @nodoc
+class _$ViewsReceivedCopyWithImpl<$Res>
+    extends _$AppWatchEventCopyWithImpl<$Res>
+    implements $ViewsReceivedCopyWith<$Res> {
+  _$ViewsReceivedCopyWithImpl(
+      ViewsReceived _value, $Res Function(ViewsReceived) _then)
+      : super(_value, (v) => _then(v as ViewsReceived));
+
+  @override
+  ViewsReceived get _value => super._value as ViewsReceived;
+
+  @override
+  $Res call({
+    Object? viewsOrFail = freezed,
+  }) {
+    return _then(ViewsReceived(
+      viewsOrFail == freezed
+          ? _value.viewsOrFail
+          : viewsOrFail // ignore: cast_nullable_to_non_nullable
+              as Either<List<View>, WorkspaceError>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$ViewsReceived implements ViewsReceived {
+  const _$ViewsReceived(this.viewsOrFail);
+
+  @override
+  final Either<List<View>, WorkspaceError> viewsOrFail;
+
+  @override
+  String toString() {
+    return 'AppWatchEvent.viewsReceived(viewsOrFail: $viewsOrFail)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is ViewsReceived &&
+            (identical(other.viewsOrFail, viewsOrFail) ||
+                const DeepCollectionEquality()
+                    .equals(other.viewsOrFail, viewsOrFail)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(viewsOrFail);
+
+  @JsonKey(ignore: true)
+  @override
+  $ViewsReceivedCopyWith<ViewsReceived> get copyWith =>
+      _$ViewsReceivedCopyWithImpl<ViewsReceived>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() started,
+    required TResult Function(Either<List<View>, WorkspaceError> viewsOrFail)
+        viewsReceived,
+  }) {
+    return viewsReceived(viewsOrFail);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? started,
+    TResult Function(Either<List<View>, WorkspaceError> viewsOrFail)?
+        viewsReceived,
+    required TResult orElse(),
+  }) {
+    if (viewsReceived != null) {
+      return viewsReceived(viewsOrFail);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Started value) started,
+    required TResult Function(ViewsReceived value) viewsReceived,
+  }) {
+    return viewsReceived(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Started value)? started,
+    TResult Function(ViewsReceived value)? viewsReceived,
+    required TResult orElse(),
+  }) {
+    if (viewsReceived != null) {
+      return viewsReceived(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class ViewsReceived implements AppWatchEvent {
+  const factory ViewsReceived(Either<List<View>, WorkspaceError> viewsOrFail) =
+      _$ViewsReceived;
+
+  Either<List<View>, WorkspaceError> get viewsOrFail =>
+      throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $ViewsReceivedCopyWith<ViewsReceived> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+class _$AppWatchStateTearOff {
+  const _$AppWatchStateTearOff();
+
+  _Initial initial() {
+    return const _Initial();
+  }
+
+  _LoadViews loadViews(List<View> views) {
+    return _LoadViews(
+      views,
+    );
+  }
+
+  _LoadFail loadFail(WorkspaceError error) {
+    return _LoadFail(
+      error,
+    );
+  }
+}
+
+/// @nodoc
+const $AppWatchState = _$AppWatchStateTearOff();
+
+/// @nodoc
+mixin _$AppWatchState {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(List<View> views) loadViews,
+    required TResult Function(WorkspaceError error) loadFail,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(List<View> views)? loadViews,
+    TResult Function(WorkspaceError error)? loadFail,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(_LoadViews value) loadViews,
+    required TResult Function(_LoadFail value) loadFail,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(_LoadViews value)? loadViews,
+    TResult Function(_LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $AppWatchStateCopyWith<$Res> {
+  factory $AppWatchStateCopyWith(
+          AppWatchState value, $Res Function(AppWatchState) then) =
+      _$AppWatchStateCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$AppWatchStateCopyWithImpl<$Res>
+    implements $AppWatchStateCopyWith<$Res> {
+  _$AppWatchStateCopyWithImpl(this._value, this._then);
+
+  final AppWatchState _value;
+  // ignore: unused_field
+  final $Res Function(AppWatchState) _then;
+}
+
+/// @nodoc
+abstract class _$InitialCopyWith<$Res> {
+  factory _$InitialCopyWith(_Initial value, $Res Function(_Initial) then) =
+      __$InitialCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class __$InitialCopyWithImpl<$Res> extends _$AppWatchStateCopyWithImpl<$Res>
+    implements _$InitialCopyWith<$Res> {
+  __$InitialCopyWithImpl(_Initial _value, $Res Function(_Initial) _then)
+      : super(_value, (v) => _then(v as _Initial));
+
+  @override
+  _Initial get _value => super._value as _Initial;
+}
+
+/// @nodoc
+
+class _$_Initial implements _Initial {
+  const _$_Initial();
+
+  @override
+  String toString() {
+    return 'AppWatchState.initial()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is _Initial);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(List<View> views) loadViews,
+    required TResult Function(WorkspaceError error) loadFail,
+  }) {
+    return initial();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(List<View> views)? loadViews,
+    TResult Function(WorkspaceError error)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(_LoadViews value) loadViews,
+    required TResult Function(_LoadFail value) loadFail,
+  }) {
+    return initial(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(_LoadViews value)? loadViews,
+    TResult Function(_LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _Initial implements AppWatchState {
+  const factory _Initial() = _$_Initial;
+}
+
+/// @nodoc
+abstract class _$LoadViewsCopyWith<$Res> {
+  factory _$LoadViewsCopyWith(
+          _LoadViews value, $Res Function(_LoadViews) then) =
+      __$LoadViewsCopyWithImpl<$Res>;
+  $Res call({List<View> views});
+}
+
+/// @nodoc
+class __$LoadViewsCopyWithImpl<$Res> extends _$AppWatchStateCopyWithImpl<$Res>
+    implements _$LoadViewsCopyWith<$Res> {
+  __$LoadViewsCopyWithImpl(_LoadViews _value, $Res Function(_LoadViews) _then)
+      : super(_value, (v) => _then(v as _LoadViews));
+
+  @override
+  _LoadViews get _value => super._value as _LoadViews;
+
+  @override
+  $Res call({
+    Object? views = freezed,
+  }) {
+    return _then(_LoadViews(
+      views == freezed
+          ? _value.views
+          : views // ignore: cast_nullable_to_non_nullable
+              as List<View>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_LoadViews implements _LoadViews {
+  const _$_LoadViews(this.views);
+
+  @override
+  final List<View> views;
+
+  @override
+  String toString() {
+    return 'AppWatchState.loadViews(views: $views)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is _LoadViews &&
+            (identical(other.views, views) ||
+                const DeepCollectionEquality().equals(other.views, views)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(views);
+
+  @JsonKey(ignore: true)
+  @override
+  _$LoadViewsCopyWith<_LoadViews> get copyWith =>
+      __$LoadViewsCopyWithImpl<_LoadViews>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(List<View> views) loadViews,
+    required TResult Function(WorkspaceError error) loadFail,
+  }) {
+    return loadViews(views);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(List<View> views)? loadViews,
+    TResult Function(WorkspaceError error)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadViews != null) {
+      return loadViews(views);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(_LoadViews value) loadViews,
+    required TResult Function(_LoadFail value) loadFail,
+  }) {
+    return loadViews(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(_LoadViews value)? loadViews,
+    TResult Function(_LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadViews != null) {
+      return loadViews(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _LoadViews implements AppWatchState {
+  const factory _LoadViews(List<View> views) = _$_LoadViews;
+
+  List<View> get views => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  _$LoadViewsCopyWith<_LoadViews> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class _$LoadFailCopyWith<$Res> {
+  factory _$LoadFailCopyWith(_LoadFail value, $Res Function(_LoadFail) then) =
+      __$LoadFailCopyWithImpl<$Res>;
+  $Res call({WorkspaceError error});
+}
+
+/// @nodoc
+class __$LoadFailCopyWithImpl<$Res> extends _$AppWatchStateCopyWithImpl<$Res>
+    implements _$LoadFailCopyWith<$Res> {
+  __$LoadFailCopyWithImpl(_LoadFail _value, $Res Function(_LoadFail) _then)
+      : super(_value, (v) => _then(v as _LoadFail));
+
+  @override
+  _LoadFail get _value => super._value as _LoadFail;
+
+  @override
+  $Res call({
+    Object? error = freezed,
+  }) {
+    return _then(_LoadFail(
+      error == freezed
+          ? _value.error
+          : error // ignore: cast_nullable_to_non_nullable
+              as WorkspaceError,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_LoadFail implements _LoadFail {
+  const _$_LoadFail(this.error);
+
+  @override
+  final WorkspaceError error;
+
+  @override
+  String toString() {
+    return 'AppWatchState.loadFail(error: $error)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is _LoadFail &&
+            (identical(other.error, error) ||
+                const DeepCollectionEquality().equals(other.error, error)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(error);
+
+  @JsonKey(ignore: true)
+  @override
+  _$LoadFailCopyWith<_LoadFail> get copyWith =>
+      __$LoadFailCopyWithImpl<_LoadFail>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(List<View> views) loadViews,
+    required TResult Function(WorkspaceError error) loadFail,
+  }) {
+    return loadFail(error);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(List<View> views)? loadViews,
+    TResult Function(WorkspaceError error)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadFail != null) {
+      return loadFail(error);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(_LoadViews value) loadViews,
+    required TResult Function(_LoadFail value) loadFail,
+  }) {
+    return loadFail(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(_LoadViews value)? loadViews,
+    TResult Function(_LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadFail != null) {
+      return loadFail(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _LoadFail implements AppWatchState {
+  const factory _LoadFail(WorkspaceError error) = _$_LoadFail;
+
+  WorkspaceError get error => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  _$LoadFailCopyWith<_LoadFail> get copyWith =>
+      throw _privateConstructorUsedError;
+}

+ 39 - 0
app_flowy/lib/workspace/application/doc/doc_bloc.dart

@@ -0,0 +1,39 @@
+import 'package:app_flowy/workspace/domain/i_doc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:dartz/dartz.dart';
+
+part 'doc_bloc.freezed.dart';
+
+class DocBloc extends Bloc<DocEvent, DocState> {
+  final IDoc iDocImpl;
+
+  DocBloc(this.iDocImpl) : super(DocState.initial());
+
+  @override
+  Stream<DocState> mapEventToState(DocEvent event) async* {
+    yield* event.map(
+      initial: (e) async* {},
+      save: (Save value) async* {},
+      close: (Close value) async* {},
+    );
+  }
+}
+
+@freezed
+abstract class DocEvent with _$DocEvent {
+  const factory DocEvent.initial() = Initial;
+  const factory DocEvent.save(String jsonStr) = Save;
+  const factory DocEvent.close() = Close;
+}
+
+@freezed
+abstract class DocState implements _$DocState {
+  const factory DocState({
+    required bool isSaving,
+  }) = _DocState;
+
+  factory DocState.initial() => const DocState(
+        isSaving: false,
+      );
+}

+ 498 - 0
app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart

@@ -0,0 +1,498 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
+
+part of 'doc_bloc.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+class _$DocEventTearOff {
+  const _$DocEventTearOff();
+
+  Initial initial() {
+    return const Initial();
+  }
+
+  Save save(String jsonStr) {
+    return Save(
+      jsonStr,
+    );
+  }
+
+  Close close() {
+    return const Close();
+  }
+}
+
+/// @nodoc
+const $DocEvent = _$DocEventTearOff();
+
+/// @nodoc
+mixin _$DocEvent {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(String jsonStr) save,
+    required TResult Function() close,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(String jsonStr)? save,
+    TResult Function()? close,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+    required TResult Function(Save value) save,
+    required TResult Function(Close value) close,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(Save value)? save,
+    TResult Function(Close value)? close,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $DocEventCopyWith<$Res> {
+  factory $DocEventCopyWith(DocEvent value, $Res Function(DocEvent) then) =
+      _$DocEventCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$DocEventCopyWithImpl<$Res> implements $DocEventCopyWith<$Res> {
+  _$DocEventCopyWithImpl(this._value, this._then);
+
+  final DocEvent _value;
+  // ignore: unused_field
+  final $Res Function(DocEvent) _then;
+}
+
+/// @nodoc
+abstract class $InitialCopyWith<$Res> {
+  factory $InitialCopyWith(Initial value, $Res Function(Initial) then) =
+      _$InitialCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$InitialCopyWithImpl<$Res> extends _$DocEventCopyWithImpl<$Res>
+    implements $InitialCopyWith<$Res> {
+  _$InitialCopyWithImpl(Initial _value, $Res Function(Initial) _then)
+      : super(_value, (v) => _then(v as Initial));
+
+  @override
+  Initial get _value => super._value as Initial;
+}
+
+/// @nodoc
+
+class _$Initial implements Initial {
+  const _$Initial();
+
+  @override
+  String toString() {
+    return 'DocEvent.initial()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is Initial);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(String jsonStr) save,
+    required TResult Function() close,
+  }) {
+    return initial();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(String jsonStr)? save,
+    TResult Function()? close,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+    required TResult Function(Save value) save,
+    required TResult Function(Close value) close,
+  }) {
+    return initial(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(Save value)? save,
+    TResult Function(Close value)? close,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class Initial implements DocEvent {
+  const factory Initial() = _$Initial;
+}
+
+/// @nodoc
+abstract class $SaveCopyWith<$Res> {
+  factory $SaveCopyWith(Save value, $Res Function(Save) then) =
+      _$SaveCopyWithImpl<$Res>;
+  $Res call({String jsonStr});
+}
+
+/// @nodoc
+class _$SaveCopyWithImpl<$Res> extends _$DocEventCopyWithImpl<$Res>
+    implements $SaveCopyWith<$Res> {
+  _$SaveCopyWithImpl(Save _value, $Res Function(Save) _then)
+      : super(_value, (v) => _then(v as Save));
+
+  @override
+  Save get _value => super._value as Save;
+
+  @override
+  $Res call({
+    Object? jsonStr = freezed,
+  }) {
+    return _then(Save(
+      jsonStr == freezed
+          ? _value.jsonStr
+          : jsonStr // ignore: cast_nullable_to_non_nullable
+              as String,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$Save implements Save {
+  const _$Save(this.jsonStr);
+
+  @override
+  final String jsonStr;
+
+  @override
+  String toString() {
+    return 'DocEvent.save(jsonStr: $jsonStr)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is Save &&
+            (identical(other.jsonStr, jsonStr) ||
+                const DeepCollectionEquality().equals(other.jsonStr, jsonStr)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(jsonStr);
+
+  @JsonKey(ignore: true)
+  @override
+  $SaveCopyWith<Save> get copyWith =>
+      _$SaveCopyWithImpl<Save>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(String jsonStr) save,
+    required TResult Function() close,
+  }) {
+    return save(jsonStr);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(String jsonStr)? save,
+    TResult Function()? close,
+    required TResult orElse(),
+  }) {
+    if (save != null) {
+      return save(jsonStr);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+    required TResult Function(Save value) save,
+    required TResult Function(Close value) close,
+  }) {
+    return save(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(Save value)? save,
+    TResult Function(Close value)? close,
+    required TResult orElse(),
+  }) {
+    if (save != null) {
+      return save(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class Save implements DocEvent {
+  const factory Save(String jsonStr) = _$Save;
+
+  String get jsonStr => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $SaveCopyWith<Save> get copyWith => throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $CloseCopyWith<$Res> {
+  factory $CloseCopyWith(Close value, $Res Function(Close) then) =
+      _$CloseCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$CloseCopyWithImpl<$Res> extends _$DocEventCopyWithImpl<$Res>
+    implements $CloseCopyWith<$Res> {
+  _$CloseCopyWithImpl(Close _value, $Res Function(Close) _then)
+      : super(_value, (v) => _then(v as Close));
+
+  @override
+  Close get _value => super._value as Close;
+}
+
+/// @nodoc
+
+class _$Close implements Close {
+  const _$Close();
+
+  @override
+  String toString() {
+    return 'DocEvent.close()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is Close);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(String jsonStr) save,
+    required TResult Function() close,
+  }) {
+    return close();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(String jsonStr)? save,
+    TResult Function()? close,
+    required TResult orElse(),
+  }) {
+    if (close != null) {
+      return close();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+    required TResult Function(Save value) save,
+    required TResult Function(Close value) close,
+  }) {
+    return close(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(Save value)? save,
+    TResult Function(Close value)? close,
+    required TResult orElse(),
+  }) {
+    if (close != null) {
+      return close(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class Close implements DocEvent {
+  const factory Close() = _$Close;
+}
+
+/// @nodoc
+class _$DocStateTearOff {
+  const _$DocStateTearOff();
+
+  _DocState call({required bool isSaving}) {
+    return _DocState(
+      isSaving: isSaving,
+    );
+  }
+}
+
+/// @nodoc
+const $DocState = _$DocStateTearOff();
+
+/// @nodoc
+mixin _$DocState {
+  bool get isSaving => throw _privateConstructorUsedError;
+
+  @JsonKey(ignore: true)
+  $DocStateCopyWith<DocState> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $DocStateCopyWith<$Res> {
+  factory $DocStateCopyWith(DocState value, $Res Function(DocState) then) =
+      _$DocStateCopyWithImpl<$Res>;
+  $Res call({bool isSaving});
+}
+
+/// @nodoc
+class _$DocStateCopyWithImpl<$Res> implements $DocStateCopyWith<$Res> {
+  _$DocStateCopyWithImpl(this._value, this._then);
+
+  final DocState _value;
+  // ignore: unused_field
+  final $Res Function(DocState) _then;
+
+  @override
+  $Res call({
+    Object? isSaving = freezed,
+  }) {
+    return _then(_value.copyWith(
+      isSaving: isSaving == freezed
+          ? _value.isSaving
+          : isSaving // ignore: cast_nullable_to_non_nullable
+              as bool,
+    ));
+  }
+}
+
+/// @nodoc
+abstract class _$DocStateCopyWith<$Res> implements $DocStateCopyWith<$Res> {
+  factory _$DocStateCopyWith(_DocState value, $Res Function(_DocState) then) =
+      __$DocStateCopyWithImpl<$Res>;
+  @override
+  $Res call({bool isSaving});
+}
+
+/// @nodoc
+class __$DocStateCopyWithImpl<$Res> extends _$DocStateCopyWithImpl<$Res>
+    implements _$DocStateCopyWith<$Res> {
+  __$DocStateCopyWithImpl(_DocState _value, $Res Function(_DocState) _then)
+      : super(_value, (v) => _then(v as _DocState));
+
+  @override
+  _DocState get _value => super._value as _DocState;
+
+  @override
+  $Res call({
+    Object? isSaving = freezed,
+  }) {
+    return _then(_DocState(
+      isSaving: isSaving == freezed
+          ? _value.isSaving
+          : isSaving // ignore: cast_nullable_to_non_nullable
+              as bool,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_DocState implements _DocState {
+  const _$_DocState({required this.isSaving});
+
+  @override
+  final bool isSaving;
+
+  @override
+  String toString() {
+    return 'DocState(isSaving: $isSaving)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is _DocState &&
+            (identical(other.isSaving, isSaving) ||
+                const DeepCollectionEquality()
+                    .equals(other.isSaving, isSaving)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(isSaving);
+
+  @JsonKey(ignore: true)
+  @override
+  _$DocStateCopyWith<_DocState> get copyWith =>
+      __$DocStateCopyWithImpl<_DocState>(this, _$identity);
+}
+
+abstract class _DocState implements DocState {
+  const factory _DocState({required bool isSaving}) = _$_DocState;
+
+  @override
+  bool get isSaving => throw _privateConstructorUsedError;
+  @override
+  @JsonKey(ignore: true)
+  _$DocStateCopyWith<_DocState> get copyWith =>
+      throw _privateConstructorUsedError;
+}

+ 22 - 3
app_flowy/lib/home/application/edit_pannel/edit_pannel_bloc.dart → app_flowy/lib/workspace/application/edit_pannel/edit_pannel_bloc.dart

@@ -1,12 +1,10 @@
-import 'package:app_flowy/home/domain/edit_context.dart';
+import 'package:app_flowy/workspace/domain/edit_context.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flutter/material.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 // ignore: import_of_legacy_library_into_null_safe
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-part 'edit_pannel_event.dart';
-part 'edit_pannel_state.dart';
 part 'edit_pannel_bloc.freezed.dart';
 
 class EditPannelBloc extends Bloc<EditPannelEvent, EditPannelState> {
@@ -26,3 +24,24 @@ class EditPannelBloc extends Bloc<EditPannelEvent, EditPannelState> {
     );
   }
 }
+
+@freezed
+abstract class EditPannelEvent with _$EditPannelEvent {
+  const factory EditPannelEvent.startEdit(EditPannelContext context) =
+      _StartEdit;
+
+  const factory EditPannelEvent.endEdit(EditPannelContext context) = _EndEdit;
+}
+
+@freezed
+abstract class EditPannelState implements _$EditPannelState {
+  const factory EditPannelState({
+    required bool isEditing,
+    required Option<EditPannelContext> editContext,
+  }) = _EditPannelState;
+
+  factory EditPannelState.initial() => EditPannelState(
+        isEditing: false,
+        editContext: none(),
+      );
+}

+ 0 - 0
app_flowy/lib/home/application/edit_pannel/edit_pannel_bloc.freezed.dart → app_flowy/lib/workspace/application/edit_pannel/edit_pannel_bloc.freezed.dart


+ 58 - 0
app_flowy/lib/workspace/application/home/home_bloc.dart

@@ -0,0 +1,58 @@
+import 'package:app_flowy/workspace/domain/edit_context.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:dartz/dartz.dart';
+part 'home_bloc.freezed.dart';
+
+class HomeBloc extends Bloc<HomeEvent, HomeState> {
+  HomeBloc() : super(HomeState.initial());
+
+  @override
+  Stream<HomeState> mapEventToState(
+    HomeEvent event,
+  ) async* {
+    yield* event.map(
+      showLoading: (e) async* {
+        yield state.copyWith(isLoading: e.isLoading);
+      },
+      setEditPannel: (e) async* {
+        yield state.copyWith(editContext: some(e.editContext));
+      },
+      dismissEditPannel: (value) async* {
+        yield state.copyWith(editContext: none());
+      },
+      forceCollapse: (e) async* {
+        yield state.copyWith(forceCollapse: e.forceCollapse);
+      },
+    );
+  }
+
+  @override
+  Future<void> close() {
+    return super.close();
+  }
+}
+
+@freezed
+abstract class HomeEvent with _$HomeEvent {
+  const factory HomeEvent.showLoading(bool isLoading) = _ShowLoading;
+  const factory HomeEvent.forceCollapse(bool forceCollapse) = _ForceCollapse;
+  const factory HomeEvent.setEditPannel(EditPannelContext editContext) =
+      _ShowEditPannel;
+  const factory HomeEvent.dismissEditPannel() = _DismissEditPannel;
+}
+
+@freezed
+abstract class HomeState implements _$HomeState {
+  const factory HomeState({
+    required bool isLoading,
+    required bool forceCollapse,
+    required Option<EditPannelContext> editContext,
+  }) = _HomeState;
+
+  factory HomeState.initial() => HomeState(
+        isLoading: false,
+        forceCollapse: false,
+        editContext: none(),
+      );
+}

+ 81 - 256
app_flowy/lib/home/application/home_bloc.freezed.dart → app_flowy/lib/workspace/application/home/home_bloc.freezed.dart

@@ -22,15 +22,9 @@ class _$HomeEventTearOff {
     );
   }
 
-  _ShowMenu showMenu(bool isShow) {
-    return _ShowMenu(
-      isShow,
-    );
-  }
-
-  SetCurrentPage setPage(PageContext context) {
-    return SetCurrentPage(
-      context,
+  _ForceCollapse forceCollapse(bool forceCollapse) {
+    return _ForceCollapse(
+      forceCollapse,
     );
   }
 
@@ -53,8 +47,7 @@ mixin _$HomeEvent {
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
     required TResult Function(bool isLoading) showLoading,
-    required TResult Function(bool isShow) showMenu,
-    required TResult Function(PageContext context) setPage,
+    required TResult Function(bool forceCollapse) forceCollapse,
     required TResult Function(EditPannelContext editContext) setEditPannel,
     required TResult Function() dismissEditPannel,
   }) =>
@@ -62,8 +55,7 @@ mixin _$HomeEvent {
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function(bool isLoading)? showLoading,
-    TResult Function(bool isShow)? showMenu,
-    TResult Function(PageContext context)? setPage,
+    TResult Function(bool forceCollapse)? forceCollapse,
     TResult Function(EditPannelContext editContext)? setEditPannel,
     TResult Function()? dismissEditPannel,
     required TResult orElse(),
@@ -72,8 +64,7 @@ mixin _$HomeEvent {
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
     required TResult Function(_ShowLoading value) showLoading,
-    required TResult Function(_ShowMenu value) showMenu,
-    required TResult Function(SetCurrentPage value) setPage,
+    required TResult Function(_ForceCollapse value) forceCollapse,
     required TResult Function(_ShowEditPannel value) setEditPannel,
     required TResult Function(_DismissEditPannel value) dismissEditPannel,
   }) =>
@@ -81,8 +72,7 @@ mixin _$HomeEvent {
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(_ShowLoading value)? showLoading,
-    TResult Function(_ShowMenu value)? showMenu,
-    TResult Function(SetCurrentPage value)? setPage,
+    TResult Function(_ForceCollapse value)? forceCollapse,
     TResult Function(_ShowEditPannel value)? setEditPannel,
     TResult Function(_DismissEditPannel value)? dismissEditPannel,
     required TResult orElse(),
@@ -171,8 +161,7 @@ class _$_ShowLoading implements _ShowLoading {
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
     required TResult Function(bool isLoading) showLoading,
-    required TResult Function(bool isShow) showMenu,
-    required TResult Function(PageContext context) setPage,
+    required TResult Function(bool forceCollapse) forceCollapse,
     required TResult Function(EditPannelContext editContext) setEditPannel,
     required TResult Function() dismissEditPannel,
   }) {
@@ -183,8 +172,7 @@ class _$_ShowLoading implements _ShowLoading {
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function(bool isLoading)? showLoading,
-    TResult Function(bool isShow)? showMenu,
-    TResult Function(PageContext context)? setPage,
+    TResult Function(bool forceCollapse)? forceCollapse,
     TResult Function(EditPannelContext editContext)? setEditPannel,
     TResult Function()? dismissEditPannel,
     required TResult orElse(),
@@ -199,8 +187,7 @@ class _$_ShowLoading implements _ShowLoading {
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
     required TResult Function(_ShowLoading value) showLoading,
-    required TResult Function(_ShowMenu value) showMenu,
-    required TResult Function(SetCurrentPage value) setPage,
+    required TResult Function(_ForceCollapse value) forceCollapse,
     required TResult Function(_ShowEditPannel value) setEditPannel,
     required TResult Function(_DismissEditPannel value) dismissEditPannel,
   }) {
@@ -211,8 +198,7 @@ class _$_ShowLoading implements _ShowLoading {
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(_ShowLoading value)? showLoading,
-    TResult Function(_ShowMenu value)? showMenu,
-    TResult Function(SetCurrentPage value)? setPage,
+    TResult Function(_ForceCollapse value)? forceCollapse,
     TResult Function(_ShowEditPannel value)? setEditPannel,
     TResult Function(_DismissEditPannel value)? dismissEditPannel,
     required TResult orElse(),
@@ -234,29 +220,31 @@ abstract class _ShowLoading implements HomeEvent {
 }
 
 /// @nodoc
-abstract class _$ShowMenuCopyWith<$Res> {
-  factory _$ShowMenuCopyWith(_ShowMenu value, $Res Function(_ShowMenu) then) =
-      __$ShowMenuCopyWithImpl<$Res>;
-  $Res call({bool isShow});
+abstract class _$ForceCollapseCopyWith<$Res> {
+  factory _$ForceCollapseCopyWith(
+          _ForceCollapse value, $Res Function(_ForceCollapse) then) =
+      __$ForceCollapseCopyWithImpl<$Res>;
+  $Res call({bool forceCollapse});
 }
 
 /// @nodoc
-class __$ShowMenuCopyWithImpl<$Res> extends _$HomeEventCopyWithImpl<$Res>
-    implements _$ShowMenuCopyWith<$Res> {
-  __$ShowMenuCopyWithImpl(_ShowMenu _value, $Res Function(_ShowMenu) _then)
-      : super(_value, (v) => _then(v as _ShowMenu));
+class __$ForceCollapseCopyWithImpl<$Res> extends _$HomeEventCopyWithImpl<$Res>
+    implements _$ForceCollapseCopyWith<$Res> {
+  __$ForceCollapseCopyWithImpl(
+      _ForceCollapse _value, $Res Function(_ForceCollapse) _then)
+      : super(_value, (v) => _then(v as _ForceCollapse));
 
   @override
-  _ShowMenu get _value => super._value as _ShowMenu;
+  _ForceCollapse get _value => super._value as _ForceCollapse;
 
   @override
   $Res call({
-    Object? isShow = freezed,
+    Object? forceCollapse = freezed,
   }) {
-    return _then(_ShowMenu(
-      isShow == freezed
-          ? _value.isShow
-          : isShow // ignore: cast_nullable_to_non_nullable
+    return _then(_ForceCollapse(
+      forceCollapse == freezed
+          ? _value.forceCollapse
+          : forceCollapse // ignore: cast_nullable_to_non_nullable
               as bool,
     ));
   }
@@ -264,185 +252,57 @@ class __$ShowMenuCopyWithImpl<$Res> extends _$HomeEventCopyWithImpl<$Res>
 
 /// @nodoc
 
-class _$_ShowMenu implements _ShowMenu {
-  const _$_ShowMenu(this.isShow);
+class _$_ForceCollapse implements _ForceCollapse {
+  const _$_ForceCollapse(this.forceCollapse);
 
   @override
-  final bool isShow;
+  final bool forceCollapse;
 
   @override
   String toString() {
-    return 'HomeEvent.showMenu(isShow: $isShow)';
+    return 'HomeEvent.forceCollapse(forceCollapse: $forceCollapse)';
   }
 
   @override
   bool operator ==(dynamic other) {
     return identical(this, other) ||
-        (other is _ShowMenu &&
-            (identical(other.isShow, isShow) ||
-                const DeepCollectionEquality().equals(other.isShow, isShow)));
-  }
-
-  @override
-  int get hashCode =>
-      runtimeType.hashCode ^ const DeepCollectionEquality().hash(isShow);
-
-  @JsonKey(ignore: true)
-  @override
-  _$ShowMenuCopyWith<_ShowMenu> get copyWith =>
-      __$ShowMenuCopyWithImpl<_ShowMenu>(this, _$identity);
-
-  @override
-  @optionalTypeArgs
-  TResult when<TResult extends Object?>({
-    required TResult Function(bool isLoading) showLoading,
-    required TResult Function(bool isShow) showMenu,
-    required TResult Function(PageContext context) setPage,
-    required TResult Function(EditPannelContext editContext) setEditPannel,
-    required TResult Function() dismissEditPannel,
-  }) {
-    return showMenu(isShow);
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeWhen<TResult extends Object?>({
-    TResult Function(bool isLoading)? showLoading,
-    TResult Function(bool isShow)? showMenu,
-    TResult Function(PageContext context)? setPage,
-    TResult Function(EditPannelContext editContext)? setEditPannel,
-    TResult Function()? dismissEditPannel,
-    required TResult orElse(),
-  }) {
-    if (showMenu != null) {
-      return showMenu(isShow);
-    }
-    return orElse();
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult map<TResult extends Object?>({
-    required TResult Function(_ShowLoading value) showLoading,
-    required TResult Function(_ShowMenu value) showMenu,
-    required TResult Function(SetCurrentPage value) setPage,
-    required TResult Function(_ShowEditPannel value) setEditPannel,
-    required TResult Function(_DismissEditPannel value) dismissEditPannel,
-  }) {
-    return showMenu(this);
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeMap<TResult extends Object?>({
-    TResult Function(_ShowLoading value)? showLoading,
-    TResult Function(_ShowMenu value)? showMenu,
-    TResult Function(SetCurrentPage value)? setPage,
-    TResult Function(_ShowEditPannel value)? setEditPannel,
-    TResult Function(_DismissEditPannel value)? dismissEditPannel,
-    required TResult orElse(),
-  }) {
-    if (showMenu != null) {
-      return showMenu(this);
-    }
-    return orElse();
-  }
-}
-
-abstract class _ShowMenu implements HomeEvent {
-  const factory _ShowMenu(bool isShow) = _$_ShowMenu;
-
-  bool get isShow => throw _privateConstructorUsedError;
-  @JsonKey(ignore: true)
-  _$ShowMenuCopyWith<_ShowMenu> get copyWith =>
-      throw _privateConstructorUsedError;
-}
-
-/// @nodoc
-abstract class $SetCurrentPageCopyWith<$Res> {
-  factory $SetCurrentPageCopyWith(
-          SetCurrentPage value, $Res Function(SetCurrentPage) then) =
-      _$SetCurrentPageCopyWithImpl<$Res>;
-  $Res call({PageContext context});
-}
-
-/// @nodoc
-class _$SetCurrentPageCopyWithImpl<$Res> extends _$HomeEventCopyWithImpl<$Res>
-    implements $SetCurrentPageCopyWith<$Res> {
-  _$SetCurrentPageCopyWithImpl(
-      SetCurrentPage _value, $Res Function(SetCurrentPage) _then)
-      : super(_value, (v) => _then(v as SetCurrentPage));
-
-  @override
-  SetCurrentPage get _value => super._value as SetCurrentPage;
-
-  @override
-  $Res call({
-    Object? context = freezed,
-  }) {
-    return _then(SetCurrentPage(
-      context == freezed
-          ? _value.context
-          : context // ignore: cast_nullable_to_non_nullable
-              as PageContext,
-    ));
-  }
-}
-
-/// @nodoc
-
-class _$SetCurrentPage implements SetCurrentPage {
-  const _$SetCurrentPage(this.context);
-
-  @override
-  final PageContext context;
-
-  @override
-  String toString() {
-    return 'HomeEvent.setPage(context: $context)';
-  }
-
-  @override
-  bool operator ==(dynamic other) {
-    return identical(this, other) ||
-        (other is SetCurrentPage &&
-            (identical(other.context, context) ||
-                const DeepCollectionEquality().equals(other.context, context)));
+        (other is _ForceCollapse &&
+            (identical(other.forceCollapse, forceCollapse) ||
+                const DeepCollectionEquality()
+                    .equals(other.forceCollapse, forceCollapse)));
   }
 
   @override
   int get hashCode =>
-      runtimeType.hashCode ^ const DeepCollectionEquality().hash(context);
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(forceCollapse);
 
   @JsonKey(ignore: true)
   @override
-  $SetCurrentPageCopyWith<SetCurrentPage> get copyWith =>
-      _$SetCurrentPageCopyWithImpl<SetCurrentPage>(this, _$identity);
+  _$ForceCollapseCopyWith<_ForceCollapse> get copyWith =>
+      __$ForceCollapseCopyWithImpl<_ForceCollapse>(this, _$identity);
 
   @override
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
     required TResult Function(bool isLoading) showLoading,
-    required TResult Function(bool isShow) showMenu,
-    required TResult Function(PageContext context) setPage,
+    required TResult Function(bool forceCollapse) forceCollapse,
     required TResult Function(EditPannelContext editContext) setEditPannel,
     required TResult Function() dismissEditPannel,
   }) {
-    return setPage(context);
+    return forceCollapse(this.forceCollapse);
   }
 
   @override
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function(bool isLoading)? showLoading,
-    TResult Function(bool isShow)? showMenu,
-    TResult Function(PageContext context)? setPage,
+    TResult Function(bool forceCollapse)? forceCollapse,
     TResult Function(EditPannelContext editContext)? setEditPannel,
     TResult Function()? dismissEditPannel,
     required TResult orElse(),
   }) {
-    if (setPage != null) {
-      return setPage(context);
+    if (forceCollapse != null) {
+      return forceCollapse(this.forceCollapse);
     }
     return orElse();
   }
@@ -451,37 +311,35 @@ class _$SetCurrentPage implements SetCurrentPage {
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
     required TResult Function(_ShowLoading value) showLoading,
-    required TResult Function(_ShowMenu value) showMenu,
-    required TResult Function(SetCurrentPage value) setPage,
+    required TResult Function(_ForceCollapse value) forceCollapse,
     required TResult Function(_ShowEditPannel value) setEditPannel,
     required TResult Function(_DismissEditPannel value) dismissEditPannel,
   }) {
-    return setPage(this);
+    return forceCollapse(this);
   }
 
   @override
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(_ShowLoading value)? showLoading,
-    TResult Function(_ShowMenu value)? showMenu,
-    TResult Function(SetCurrentPage value)? setPage,
+    TResult Function(_ForceCollapse value)? forceCollapse,
     TResult Function(_ShowEditPannel value)? setEditPannel,
     TResult Function(_DismissEditPannel value)? dismissEditPannel,
     required TResult orElse(),
   }) {
-    if (setPage != null) {
-      return setPage(this);
+    if (forceCollapse != null) {
+      return forceCollapse(this);
     }
     return orElse();
   }
 }
 
-abstract class SetCurrentPage implements HomeEvent {
-  const factory SetCurrentPage(PageContext context) = _$SetCurrentPage;
+abstract class _ForceCollapse implements HomeEvent {
+  const factory _ForceCollapse(bool forceCollapse) = _$_ForceCollapse;
 
-  PageContext get context => throw _privateConstructorUsedError;
+  bool get forceCollapse => throw _privateConstructorUsedError;
   @JsonKey(ignore: true)
-  $SetCurrentPageCopyWith<SetCurrentPage> get copyWith =>
+  _$ForceCollapseCopyWith<_ForceCollapse> get copyWith =>
       throw _privateConstructorUsedError;
 }
 
@@ -551,8 +409,7 @@ class _$_ShowEditPannel implements _ShowEditPannel {
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
     required TResult Function(bool isLoading) showLoading,
-    required TResult Function(bool isShow) showMenu,
-    required TResult Function(PageContext context) setPage,
+    required TResult Function(bool forceCollapse) forceCollapse,
     required TResult Function(EditPannelContext editContext) setEditPannel,
     required TResult Function() dismissEditPannel,
   }) {
@@ -563,8 +420,7 @@ class _$_ShowEditPannel implements _ShowEditPannel {
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function(bool isLoading)? showLoading,
-    TResult Function(bool isShow)? showMenu,
-    TResult Function(PageContext context)? setPage,
+    TResult Function(bool forceCollapse)? forceCollapse,
     TResult Function(EditPannelContext editContext)? setEditPannel,
     TResult Function()? dismissEditPannel,
     required TResult orElse(),
@@ -579,8 +435,7 @@ class _$_ShowEditPannel implements _ShowEditPannel {
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
     required TResult Function(_ShowLoading value) showLoading,
-    required TResult Function(_ShowMenu value) showMenu,
-    required TResult Function(SetCurrentPage value) setPage,
+    required TResult Function(_ForceCollapse value) forceCollapse,
     required TResult Function(_ShowEditPannel value) setEditPannel,
     required TResult Function(_DismissEditPannel value) dismissEditPannel,
   }) {
@@ -591,8 +446,7 @@ class _$_ShowEditPannel implements _ShowEditPannel {
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(_ShowLoading value)? showLoading,
-    TResult Function(_ShowMenu value)? showMenu,
-    TResult Function(SetCurrentPage value)? setPage,
+    TResult Function(_ForceCollapse value)? forceCollapse,
     TResult Function(_ShowEditPannel value)? setEditPannel,
     TResult Function(_DismissEditPannel value)? dismissEditPannel,
     required TResult orElse(),
@@ -655,8 +509,7 @@ class _$_DismissEditPannel implements _DismissEditPannel {
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
     required TResult Function(bool isLoading) showLoading,
-    required TResult Function(bool isShow) showMenu,
-    required TResult Function(PageContext context) setPage,
+    required TResult Function(bool forceCollapse) forceCollapse,
     required TResult Function(EditPannelContext editContext) setEditPannel,
     required TResult Function() dismissEditPannel,
   }) {
@@ -667,8 +520,7 @@ class _$_DismissEditPannel implements _DismissEditPannel {
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function(bool isLoading)? showLoading,
-    TResult Function(bool isShow)? showMenu,
-    TResult Function(PageContext context)? setPage,
+    TResult Function(bool forceCollapse)? forceCollapse,
     TResult Function(EditPannelContext editContext)? setEditPannel,
     TResult Function()? dismissEditPannel,
     required TResult orElse(),
@@ -683,8 +535,7 @@ class _$_DismissEditPannel implements _DismissEditPannel {
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
     required TResult Function(_ShowLoading value) showLoading,
-    required TResult Function(_ShowMenu value) showMenu,
-    required TResult Function(SetCurrentPage value) setPage,
+    required TResult Function(_ForceCollapse value) forceCollapse,
     required TResult Function(_ShowEditPannel value) setEditPannel,
     required TResult Function(_DismissEditPannel value) dismissEditPannel,
   }) {
@@ -695,8 +546,7 @@ class _$_DismissEditPannel implements _DismissEditPannel {
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(_ShowLoading value)? showLoading,
-    TResult Function(_ShowMenu value)? showMenu,
-    TResult Function(SetCurrentPage value)? setPage,
+    TResult Function(_ForceCollapse value)? forceCollapse,
     TResult Function(_ShowEditPannel value)? setEditPannel,
     TResult Function(_DismissEditPannel value)? dismissEditPannel,
     required TResult orElse(),
@@ -718,13 +568,11 @@ class _$HomeStateTearOff {
 
   _HomeState call(
       {required bool isLoading,
-      required bool showMenu,
-      required PageContext pageContext,
+      required bool forceCollapse,
       required Option<EditPannelContext> editContext}) {
     return _HomeState(
       isLoading: isLoading,
-      showMenu: showMenu,
-      pageContext: pageContext,
+      forceCollapse: forceCollapse,
       editContext: editContext,
     );
   }
@@ -736,8 +584,7 @@ const $HomeState = _$HomeStateTearOff();
 /// @nodoc
 mixin _$HomeState {
   bool get isLoading => throw _privateConstructorUsedError;
-  bool get showMenu => throw _privateConstructorUsedError;
-  PageContext get pageContext => throw _privateConstructorUsedError;
+  bool get forceCollapse => throw _privateConstructorUsedError;
   Option<EditPannelContext> get editContext =>
       throw _privateConstructorUsedError;
 
@@ -752,8 +599,7 @@ abstract class $HomeStateCopyWith<$Res> {
       _$HomeStateCopyWithImpl<$Res>;
   $Res call(
       {bool isLoading,
-      bool showMenu,
-      PageContext pageContext,
+      bool forceCollapse,
       Option<EditPannelContext> editContext});
 }
 
@@ -768,8 +614,7 @@ class _$HomeStateCopyWithImpl<$Res> implements $HomeStateCopyWith<$Res> {
   @override
   $Res call({
     Object? isLoading = freezed,
-    Object? showMenu = freezed,
-    Object? pageContext = freezed,
+    Object? forceCollapse = freezed,
     Object? editContext = freezed,
   }) {
     return _then(_value.copyWith(
@@ -777,14 +622,10 @@ class _$HomeStateCopyWithImpl<$Res> implements $HomeStateCopyWith<$Res> {
           ? _value.isLoading
           : isLoading // ignore: cast_nullable_to_non_nullable
               as bool,
-      showMenu: showMenu == freezed
-          ? _value.showMenu
-          : showMenu // ignore: cast_nullable_to_non_nullable
+      forceCollapse: forceCollapse == freezed
+          ? _value.forceCollapse
+          : forceCollapse // ignore: cast_nullable_to_non_nullable
               as bool,
-      pageContext: pageContext == freezed
-          ? _value.pageContext
-          : pageContext // ignore: cast_nullable_to_non_nullable
-              as PageContext,
       editContext: editContext == freezed
           ? _value.editContext
           : editContext // ignore: cast_nullable_to_non_nullable
@@ -801,8 +642,7 @@ abstract class _$HomeStateCopyWith<$Res> implements $HomeStateCopyWith<$Res> {
   @override
   $Res call(
       {bool isLoading,
-      bool showMenu,
-      PageContext pageContext,
+      bool forceCollapse,
       Option<EditPannelContext> editContext});
 }
 
@@ -818,8 +658,7 @@ class __$HomeStateCopyWithImpl<$Res> extends _$HomeStateCopyWithImpl<$Res>
   @override
   $Res call({
     Object? isLoading = freezed,
-    Object? showMenu = freezed,
-    Object? pageContext = freezed,
+    Object? forceCollapse = freezed,
     Object? editContext = freezed,
   }) {
     return _then(_HomeState(
@@ -827,14 +666,10 @@ class __$HomeStateCopyWithImpl<$Res> extends _$HomeStateCopyWithImpl<$Res>
           ? _value.isLoading
           : isLoading // ignore: cast_nullable_to_non_nullable
               as bool,
-      showMenu: showMenu == freezed
-          ? _value.showMenu
-          : showMenu // ignore: cast_nullable_to_non_nullable
+      forceCollapse: forceCollapse == freezed
+          ? _value.forceCollapse
+          : forceCollapse // ignore: cast_nullable_to_non_nullable
               as bool,
-      pageContext: pageContext == freezed
-          ? _value.pageContext
-          : pageContext // ignore: cast_nullable_to_non_nullable
-              as PageContext,
       editContext: editContext == freezed
           ? _value.editContext
           : editContext // ignore: cast_nullable_to_non_nullable
@@ -848,22 +683,19 @@ class __$HomeStateCopyWithImpl<$Res> extends _$HomeStateCopyWithImpl<$Res>
 class _$_HomeState implements _HomeState {
   const _$_HomeState(
       {required this.isLoading,
-      required this.showMenu,
-      required this.pageContext,
+      required this.forceCollapse,
       required this.editContext});
 
   @override
   final bool isLoading;
   @override
-  final bool showMenu;
-  @override
-  final PageContext pageContext;
+  final bool forceCollapse;
   @override
   final Option<EditPannelContext> editContext;
 
   @override
   String toString() {
-    return 'HomeState(isLoading: $isLoading, showMenu: $showMenu, pageContext: $pageContext, editContext: $editContext)';
+    return 'HomeState(isLoading: $isLoading, forceCollapse: $forceCollapse, editContext: $editContext)';
   }
 
   @override
@@ -873,12 +705,9 @@ class _$_HomeState implements _HomeState {
             (identical(other.isLoading, isLoading) ||
                 const DeepCollectionEquality()
                     .equals(other.isLoading, isLoading)) &&
-            (identical(other.showMenu, showMenu) ||
+            (identical(other.forceCollapse, forceCollapse) ||
                 const DeepCollectionEquality()
-                    .equals(other.showMenu, showMenu)) &&
-            (identical(other.pageContext, pageContext) ||
-                const DeepCollectionEquality()
-                    .equals(other.pageContext, pageContext)) &&
+                    .equals(other.forceCollapse, forceCollapse)) &&
             (identical(other.editContext, editContext) ||
                 const DeepCollectionEquality()
                     .equals(other.editContext, editContext)));
@@ -888,8 +717,7 @@ class _$_HomeState implements _HomeState {
   int get hashCode =>
       runtimeType.hashCode ^
       const DeepCollectionEquality().hash(isLoading) ^
-      const DeepCollectionEquality().hash(showMenu) ^
-      const DeepCollectionEquality().hash(pageContext) ^
+      const DeepCollectionEquality().hash(forceCollapse) ^
       const DeepCollectionEquality().hash(editContext);
 
   @JsonKey(ignore: true)
@@ -901,16 +729,13 @@ class _$_HomeState implements _HomeState {
 abstract class _HomeState implements HomeState {
   const factory _HomeState(
       {required bool isLoading,
-      required bool showMenu,
-      required PageContext pageContext,
+      required bool forceCollapse,
       required Option<EditPannelContext> editContext}) = _$_HomeState;
 
   @override
   bool get isLoading => throw _privateConstructorUsedError;
   @override
-  bool get showMenu => throw _privateConstructorUsedError;
-  @override
-  PageContext get pageContext => throw _privateConstructorUsedError;
+  bool get forceCollapse => throw _privateConstructorUsedError;
   @override
   Option<EditPannelContext> get editContext =>
       throw _privateConstructorUsedError;

+ 12 - 3
app_flowy/lib/home/application/watcher/home_watcher_bloc.dart → app_flowy/lib/workspace/application/home/home_watcher_bloc.dart

@@ -1,10 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
-// ignore: import_of_legacy_library_into_null_safe
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-part 'home_watcher_event.dart';
-part 'home_watcher_state.dart';
 part 'home_watcher_bloc.freezed.dart';
 
 class HomeWatcherBloc extends Bloc<HomeWatcherEvent, HomeWatcherState> {
@@ -17,3 +14,15 @@ class HomeWatcherBloc extends Bloc<HomeWatcherEvent, HomeWatcherState> {
     yield state;
   }
 }
+
+@freezed
+abstract class HomeWatcherEvent with _$HomeWatcherEvent {
+  const factory HomeWatcherEvent.started(String workspaceId) = _Started;
+  const factory HomeWatcherEvent.stop(String workspaceId) = _Stop;
+}
+
+@freezed
+abstract class HomeWatcherState with _$HomeWatcherState {
+  const factory HomeWatcherState.initial() = _Initial;
+  const factory HomeWatcherState.loading() = _Loading;
+}

+ 0 - 0
app_flowy/lib/home/application/watcher/home_watcher_bloc.freezed.dart → app_flowy/lib/workspace/application/home/home_watcher_bloc.freezed.dart


+ 86 - 0
app_flowy/lib/workspace/application/menu/menu_bloc.dart

@@ -0,0 +1,86 @@
+import 'dart:async';
+import 'package:app_flowy/workspace/domain/i_workspace.dart';
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+part 'menu_bloc.freezed.dart';
+
+class MenuBloc extends Bloc<MenuEvent, MenuState> {
+  final IWorkspace iWorkspaceImpl;
+  MenuBloc(this.iWorkspaceImpl) : super(MenuState.initial());
+
+  @override
+  Stream<MenuState> mapEventToState(
+    MenuEvent event,
+  ) async* {
+    yield* event.map(
+      initial: (value) async* {
+        yield* _fetchApps();
+      },
+      collapse: (e) async* {
+        final isCollapse = state.isCollapse;
+        yield state.copyWith(isCollapse: !isCollapse);
+      },
+      openPage: (OpenPage e) async* {
+        yield* _performActionOnOpenPage(e);
+      },
+      createApp: (CreateApp event) async* {
+        yield* _performActionOnCreateApp(event);
+      },
+    );
+  }
+
+  Stream<MenuState> _performActionOnOpenPage(OpenPage e) async* {
+    yield state.copyWith(stackView: e.stackView);
+  }
+
+  Stream<MenuState> _performActionOnCreateApp(CreateApp event) async* {
+    await iWorkspaceImpl
+        .createApp(name: event.name, desc: event.desc)
+        .then((result) async* {
+      result.fold(
+        (app) => {},
+        (error) async* {
+          yield state.copyWith(successOrFailure: right(error));
+        },
+      );
+    });
+  }
+
+  Stream<MenuState> _fetchApps() async* {
+    final appsOrFail = await iWorkspaceImpl.getApps();
+    yield appsOrFail.fold(
+      (apps) => state.copyWith(apps: some(apps)),
+      (error) => state.copyWith(successOrFailure: right(error)),
+    );
+  }
+}
+
+@freezed
+abstract class MenuEvent with _$MenuEvent {
+  const factory MenuEvent.initial() = _Initial;
+  const factory MenuEvent.collapse() = Collapse;
+  const factory MenuEvent.openPage(HomeStackView stackView) = OpenPage;
+  const factory MenuEvent.createApp(String name, {String? desc}) = CreateApp;
+}
+
+@freezed
+abstract class MenuState implements _$MenuState {
+  const factory MenuState({
+    required bool isCollapse,
+    required Option<List<App>> apps,
+    required Either<Unit, WorkspaceError> successOrFailure,
+    HomeStackView? stackView,
+  }) = _MenuState;
+
+  factory MenuState.initial() => MenuState(
+        isCollapse: false,
+        apps: none(),
+        successOrFailure: left(unit),
+      );
+}

+ 739 - 0
app_flowy/lib/workspace/application/menu/menu_bloc.freezed.dart

@@ -0,0 +1,739 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
+
+part of 'menu_bloc.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+class _$MenuEventTearOff {
+  const _$MenuEventTearOff();
+
+  _Initial initial() {
+    return const _Initial();
+  }
+
+  Collapse collapse() {
+    return const Collapse();
+  }
+
+  OpenPage openPage(HomeStackView stackView) {
+    return OpenPage(
+      stackView,
+    );
+  }
+
+  CreateApp createApp(String name, {String? desc}) {
+    return CreateApp(
+      name,
+      desc: desc,
+    );
+  }
+}
+
+/// @nodoc
+const $MenuEvent = _$MenuEventTearOff();
+
+/// @nodoc
+mixin _$MenuEvent {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function() collapse,
+    required TResult Function(HomeStackView stackView) openPage,
+    required TResult Function(String name, String? desc) createApp,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function()? collapse,
+    TResult Function(HomeStackView stackView)? openPage,
+    TResult Function(String name, String? desc)? createApp,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(Collapse value) collapse,
+    required TResult Function(OpenPage value) openPage,
+    required TResult Function(CreateApp value) createApp,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(Collapse value)? collapse,
+    TResult Function(OpenPage value)? openPage,
+    TResult Function(CreateApp value)? createApp,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $MenuEventCopyWith<$Res> {
+  factory $MenuEventCopyWith(MenuEvent value, $Res Function(MenuEvent) then) =
+      _$MenuEventCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$MenuEventCopyWithImpl<$Res> implements $MenuEventCopyWith<$Res> {
+  _$MenuEventCopyWithImpl(this._value, this._then);
+
+  final MenuEvent _value;
+  // ignore: unused_field
+  final $Res Function(MenuEvent) _then;
+}
+
+/// @nodoc
+abstract class _$InitialCopyWith<$Res> {
+  factory _$InitialCopyWith(_Initial value, $Res Function(_Initial) then) =
+      __$InitialCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class __$InitialCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
+    implements _$InitialCopyWith<$Res> {
+  __$InitialCopyWithImpl(_Initial _value, $Res Function(_Initial) _then)
+      : super(_value, (v) => _then(v as _Initial));
+
+  @override
+  _Initial get _value => super._value as _Initial;
+}
+
+/// @nodoc
+
+class _$_Initial implements _Initial {
+  const _$_Initial();
+
+  @override
+  String toString() {
+    return 'MenuEvent.initial()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is _Initial);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function() collapse,
+    required TResult Function(HomeStackView stackView) openPage,
+    required TResult Function(String name, String? desc) createApp,
+  }) {
+    return initial();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function()? collapse,
+    TResult Function(HomeStackView stackView)? openPage,
+    TResult Function(String name, String? desc)? createApp,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(Collapse value) collapse,
+    required TResult Function(OpenPage value) openPage,
+    required TResult Function(CreateApp value) createApp,
+  }) {
+    return initial(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(Collapse value)? collapse,
+    TResult Function(OpenPage value)? openPage,
+    TResult Function(CreateApp value)? createApp,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _Initial implements MenuEvent {
+  const factory _Initial() = _$_Initial;
+}
+
+/// @nodoc
+abstract class $CollapseCopyWith<$Res> {
+  factory $CollapseCopyWith(Collapse value, $Res Function(Collapse) then) =
+      _$CollapseCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$CollapseCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
+    implements $CollapseCopyWith<$Res> {
+  _$CollapseCopyWithImpl(Collapse _value, $Res Function(Collapse) _then)
+      : super(_value, (v) => _then(v as Collapse));
+
+  @override
+  Collapse get _value => super._value as Collapse;
+}
+
+/// @nodoc
+
+class _$Collapse implements Collapse {
+  const _$Collapse();
+
+  @override
+  String toString() {
+    return 'MenuEvent.collapse()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is Collapse);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function() collapse,
+    required TResult Function(HomeStackView stackView) openPage,
+    required TResult Function(String name, String? desc) createApp,
+  }) {
+    return collapse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function()? collapse,
+    TResult Function(HomeStackView stackView)? openPage,
+    TResult Function(String name, String? desc)? createApp,
+    required TResult orElse(),
+  }) {
+    if (collapse != null) {
+      return collapse();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(Collapse value) collapse,
+    required TResult Function(OpenPage value) openPage,
+    required TResult Function(CreateApp value) createApp,
+  }) {
+    return collapse(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(Collapse value)? collapse,
+    TResult Function(OpenPage value)? openPage,
+    TResult Function(CreateApp value)? createApp,
+    required TResult orElse(),
+  }) {
+    if (collapse != null) {
+      return collapse(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class Collapse implements MenuEvent {
+  const factory Collapse() = _$Collapse;
+}
+
+/// @nodoc
+abstract class $OpenPageCopyWith<$Res> {
+  factory $OpenPageCopyWith(OpenPage value, $Res Function(OpenPage) then) =
+      _$OpenPageCopyWithImpl<$Res>;
+  $Res call({HomeStackView stackView});
+}
+
+/// @nodoc
+class _$OpenPageCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
+    implements $OpenPageCopyWith<$Res> {
+  _$OpenPageCopyWithImpl(OpenPage _value, $Res Function(OpenPage) _then)
+      : super(_value, (v) => _then(v as OpenPage));
+
+  @override
+  OpenPage get _value => super._value as OpenPage;
+
+  @override
+  $Res call({
+    Object? stackView = freezed,
+  }) {
+    return _then(OpenPage(
+      stackView == freezed
+          ? _value.stackView
+          : stackView // ignore: cast_nullable_to_non_nullable
+              as HomeStackView,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$OpenPage implements OpenPage {
+  const _$OpenPage(this.stackView);
+
+  @override
+  final HomeStackView stackView;
+
+  @override
+  String toString() {
+    return 'MenuEvent.openPage(stackView: $stackView)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is OpenPage &&
+            (identical(other.stackView, stackView) ||
+                const DeepCollectionEquality()
+                    .equals(other.stackView, stackView)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(stackView);
+
+  @JsonKey(ignore: true)
+  @override
+  $OpenPageCopyWith<OpenPage> get copyWith =>
+      _$OpenPageCopyWithImpl<OpenPage>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function() collapse,
+    required TResult Function(HomeStackView stackView) openPage,
+    required TResult Function(String name, String? desc) createApp,
+  }) {
+    return openPage(stackView);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function()? collapse,
+    TResult Function(HomeStackView stackView)? openPage,
+    TResult Function(String name, String? desc)? createApp,
+    required TResult orElse(),
+  }) {
+    if (openPage != null) {
+      return openPage(stackView);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(Collapse value) collapse,
+    required TResult Function(OpenPage value) openPage,
+    required TResult Function(CreateApp value) createApp,
+  }) {
+    return openPage(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(Collapse value)? collapse,
+    TResult Function(OpenPage value)? openPage,
+    TResult Function(CreateApp value)? createApp,
+    required TResult orElse(),
+  }) {
+    if (openPage != null) {
+      return openPage(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class OpenPage implements MenuEvent {
+  const factory OpenPage(HomeStackView stackView) = _$OpenPage;
+
+  HomeStackView get stackView => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $OpenPageCopyWith<OpenPage> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $CreateAppCopyWith<$Res> {
+  factory $CreateAppCopyWith(CreateApp value, $Res Function(CreateApp) then) =
+      _$CreateAppCopyWithImpl<$Res>;
+  $Res call({String name, String? desc});
+}
+
+/// @nodoc
+class _$CreateAppCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
+    implements $CreateAppCopyWith<$Res> {
+  _$CreateAppCopyWithImpl(CreateApp _value, $Res Function(CreateApp) _then)
+      : super(_value, (v) => _then(v as CreateApp));
+
+  @override
+  CreateApp get _value => super._value as CreateApp;
+
+  @override
+  $Res call({
+    Object? name = freezed,
+    Object? desc = freezed,
+  }) {
+    return _then(CreateApp(
+      name == freezed
+          ? _value.name
+          : name // ignore: cast_nullable_to_non_nullable
+              as String,
+      desc: desc == freezed
+          ? _value.desc
+          : desc // ignore: cast_nullable_to_non_nullable
+              as String?,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$CreateApp implements CreateApp {
+  const _$CreateApp(this.name, {this.desc});
+
+  @override
+  final String name;
+  @override
+  final String? desc;
+
+  @override
+  String toString() {
+    return 'MenuEvent.createApp(name: $name, desc: $desc)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is CreateApp &&
+            (identical(other.name, name) ||
+                const DeepCollectionEquality().equals(other.name, name)) &&
+            (identical(other.desc, desc) ||
+                const DeepCollectionEquality().equals(other.desc, desc)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^
+      const DeepCollectionEquality().hash(name) ^
+      const DeepCollectionEquality().hash(desc);
+
+  @JsonKey(ignore: true)
+  @override
+  $CreateAppCopyWith<CreateApp> get copyWith =>
+      _$CreateAppCopyWithImpl<CreateApp>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function() collapse,
+    required TResult Function(HomeStackView stackView) openPage,
+    required TResult Function(String name, String? desc) createApp,
+  }) {
+    return createApp(name, desc);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function()? collapse,
+    TResult Function(HomeStackView stackView)? openPage,
+    TResult Function(String name, String? desc)? createApp,
+    required TResult orElse(),
+  }) {
+    if (createApp != null) {
+      return createApp(name, desc);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(Collapse value) collapse,
+    required TResult Function(OpenPage value) openPage,
+    required TResult Function(CreateApp value) createApp,
+  }) {
+    return createApp(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(Collapse value)? collapse,
+    TResult Function(OpenPage value)? openPage,
+    TResult Function(CreateApp value)? createApp,
+    required TResult orElse(),
+  }) {
+    if (createApp != null) {
+      return createApp(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class CreateApp implements MenuEvent {
+  const factory CreateApp(String name, {String? desc}) = _$CreateApp;
+
+  String get name => throw _privateConstructorUsedError;
+  String? get desc => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $CreateAppCopyWith<CreateApp> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+class _$MenuStateTearOff {
+  const _$MenuStateTearOff();
+
+  _MenuState call(
+      {required bool isCollapse,
+      required Option<List<App>> apps,
+      required Either<Unit, WorkspaceError> successOrFailure,
+      HomeStackView? stackView}) {
+    return _MenuState(
+      isCollapse: isCollapse,
+      apps: apps,
+      successOrFailure: successOrFailure,
+      stackView: stackView,
+    );
+  }
+}
+
+/// @nodoc
+const $MenuState = _$MenuStateTearOff();
+
+/// @nodoc
+mixin _$MenuState {
+  bool get isCollapse => throw _privateConstructorUsedError;
+  Option<List<App>> get apps => throw _privateConstructorUsedError;
+  Either<Unit, WorkspaceError> get successOrFailure =>
+      throw _privateConstructorUsedError;
+  HomeStackView? get stackView => throw _privateConstructorUsedError;
+
+  @JsonKey(ignore: true)
+  $MenuStateCopyWith<MenuState> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $MenuStateCopyWith<$Res> {
+  factory $MenuStateCopyWith(MenuState value, $Res Function(MenuState) then) =
+      _$MenuStateCopyWithImpl<$Res>;
+  $Res call(
+      {bool isCollapse,
+      Option<List<App>> apps,
+      Either<Unit, WorkspaceError> successOrFailure,
+      HomeStackView? stackView});
+}
+
+/// @nodoc
+class _$MenuStateCopyWithImpl<$Res> implements $MenuStateCopyWith<$Res> {
+  _$MenuStateCopyWithImpl(this._value, this._then);
+
+  final MenuState _value;
+  // ignore: unused_field
+  final $Res Function(MenuState) _then;
+
+  @override
+  $Res call({
+    Object? isCollapse = freezed,
+    Object? apps = freezed,
+    Object? successOrFailure = freezed,
+    Object? stackView = freezed,
+  }) {
+    return _then(_value.copyWith(
+      isCollapse: isCollapse == freezed
+          ? _value.isCollapse
+          : isCollapse // ignore: cast_nullable_to_non_nullable
+              as bool,
+      apps: apps == freezed
+          ? _value.apps
+          : apps // ignore: cast_nullable_to_non_nullable
+              as Option<List<App>>,
+      successOrFailure: successOrFailure == freezed
+          ? _value.successOrFailure
+          : successOrFailure // ignore: cast_nullable_to_non_nullable
+              as Either<Unit, WorkspaceError>,
+      stackView: stackView == freezed
+          ? _value.stackView
+          : stackView // ignore: cast_nullable_to_non_nullable
+              as HomeStackView?,
+    ));
+  }
+}
+
+/// @nodoc
+abstract class _$MenuStateCopyWith<$Res> implements $MenuStateCopyWith<$Res> {
+  factory _$MenuStateCopyWith(
+          _MenuState value, $Res Function(_MenuState) then) =
+      __$MenuStateCopyWithImpl<$Res>;
+  @override
+  $Res call(
+      {bool isCollapse,
+      Option<List<App>> apps,
+      Either<Unit, WorkspaceError> successOrFailure,
+      HomeStackView? stackView});
+}
+
+/// @nodoc
+class __$MenuStateCopyWithImpl<$Res> extends _$MenuStateCopyWithImpl<$Res>
+    implements _$MenuStateCopyWith<$Res> {
+  __$MenuStateCopyWithImpl(_MenuState _value, $Res Function(_MenuState) _then)
+      : super(_value, (v) => _then(v as _MenuState));
+
+  @override
+  _MenuState get _value => super._value as _MenuState;
+
+  @override
+  $Res call({
+    Object? isCollapse = freezed,
+    Object? apps = freezed,
+    Object? successOrFailure = freezed,
+    Object? stackView = freezed,
+  }) {
+    return _then(_MenuState(
+      isCollapse: isCollapse == freezed
+          ? _value.isCollapse
+          : isCollapse // ignore: cast_nullable_to_non_nullable
+              as bool,
+      apps: apps == freezed
+          ? _value.apps
+          : apps // ignore: cast_nullable_to_non_nullable
+              as Option<List<App>>,
+      successOrFailure: successOrFailure == freezed
+          ? _value.successOrFailure
+          : successOrFailure // ignore: cast_nullable_to_non_nullable
+              as Either<Unit, WorkspaceError>,
+      stackView: stackView == freezed
+          ? _value.stackView
+          : stackView // ignore: cast_nullable_to_non_nullable
+              as HomeStackView?,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_MenuState implements _MenuState {
+  const _$_MenuState(
+      {required this.isCollapse,
+      required this.apps,
+      required this.successOrFailure,
+      this.stackView});
+
+  @override
+  final bool isCollapse;
+  @override
+  final Option<List<App>> apps;
+  @override
+  final Either<Unit, WorkspaceError> successOrFailure;
+  @override
+  final HomeStackView? stackView;
+
+  @override
+  String toString() {
+    return 'MenuState(isCollapse: $isCollapse, apps: $apps, successOrFailure: $successOrFailure, stackView: $stackView)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is _MenuState &&
+            (identical(other.isCollapse, isCollapse) ||
+                const DeepCollectionEquality()
+                    .equals(other.isCollapse, isCollapse)) &&
+            (identical(other.apps, apps) ||
+                const DeepCollectionEquality().equals(other.apps, apps)) &&
+            (identical(other.successOrFailure, successOrFailure) ||
+                const DeepCollectionEquality()
+                    .equals(other.successOrFailure, successOrFailure)) &&
+            (identical(other.stackView, stackView) ||
+                const DeepCollectionEquality()
+                    .equals(other.stackView, stackView)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^
+      const DeepCollectionEquality().hash(isCollapse) ^
+      const DeepCollectionEquality().hash(apps) ^
+      const DeepCollectionEquality().hash(successOrFailure) ^
+      const DeepCollectionEquality().hash(stackView);
+
+  @JsonKey(ignore: true)
+  @override
+  _$MenuStateCopyWith<_MenuState> get copyWith =>
+      __$MenuStateCopyWithImpl<_MenuState>(this, _$identity);
+}
+
+abstract class _MenuState implements MenuState {
+  const factory _MenuState(
+      {required bool isCollapse,
+      required Option<List<App>> apps,
+      required Either<Unit, WorkspaceError> successOrFailure,
+      HomeStackView? stackView}) = _$_MenuState;
+
+  @override
+  bool get isCollapse => throw _privateConstructorUsedError;
+  @override
+  Option<List<App>> get apps => throw _privateConstructorUsedError;
+  @override
+  Either<Unit, WorkspaceError> get successOrFailure =>
+      throw _privateConstructorUsedError;
+  @override
+  HomeStackView? get stackView => throw _privateConstructorUsedError;
+  @override
+  @JsonKey(ignore: true)
+  _$MenuStateCopyWith<_MenuState> get copyWith =>
+      throw _privateConstructorUsedError;
+}

+ 64 - 0
app_flowy/lib/workspace/application/menu/menu_watch.dart

@@ -0,0 +1,64 @@
+import 'package:app_flowy/workspace/domain/i_workspace.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:dartz/dartz.dart';
+
+part 'menu_watch.freezed.dart';
+
+class MenuWatchBloc extends Bloc<MenuWatchEvent, MenuWatchState> {
+  final IWorkspaceWatch watcher;
+  MenuWatchBloc(this.watcher) : super(const MenuWatchState.initial());
+
+  @override
+  Stream<MenuWatchState> mapEventToState(MenuWatchEvent event) async* {
+    yield* event.map(
+      started: (_) async* {
+        watcher.startWatching(
+          addAppCallback: (appsOrFail) => _handleAppsOrFail(appsOrFail),
+        );
+      },
+      appsReceived: (e) async* {
+        yield e.appsOrFail.fold(
+          (apps) => MenuWatchState.loadApps(apps),
+          (error) => MenuWatchState.loadFail(error),
+        );
+      },
+    );
+  }
+
+  @override
+  Future<void> close() async {
+    await watcher.stopWatching();
+    return super.close();
+  }
+
+  void _handleAppsOrFail(Either<List<App>, WorkspaceError> appsOrFail) {
+    appsOrFail.fold(
+      (apps) => add(MenuWatchEvent.appsReceived(left(apps))),
+      (error) => add(MenuWatchEvent.appsReceived(right(error))),
+    );
+  }
+}
+
+@freezed
+abstract class MenuWatchEvent with _$MenuWatchEvent {
+  const factory MenuWatchEvent.started() = _Started;
+  const factory MenuWatchEvent.appsReceived(
+      Either<List<App>, WorkspaceError> appsOrFail) = AppsReceived;
+}
+
+@freezed
+abstract class MenuWatchState with _$MenuWatchState {
+  const factory MenuWatchState.initial() = _Initial;
+
+  const factory MenuWatchState.loadApps(
+    List<App> apps,
+  ) = _LoadApps;
+
+  const factory MenuWatchState.loadFail(
+    WorkspaceError error,
+  ) = _LoadFail;
+}

+ 682 - 0
app_flowy/lib/workspace/application/menu/menu_watch.freezed.dart

@@ -0,0 +1,682 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
+
+part of 'menu_watch.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+class _$MenuWatchEventTearOff {
+  const _$MenuWatchEventTearOff();
+
+  _Started started() {
+    return const _Started();
+  }
+
+  AppsReceived appsReceived(Either<List<App>, WorkspaceError> appsOrFail) {
+    return AppsReceived(
+      appsOrFail,
+    );
+  }
+}
+
+/// @nodoc
+const $MenuWatchEvent = _$MenuWatchEventTearOff();
+
+/// @nodoc
+mixin _$MenuWatchEvent {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() started,
+    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
+        appsReceived,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? started,
+    TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
+        appsReceived,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Started value) started,
+    required TResult Function(AppsReceived value) appsReceived,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Started value)? started,
+    TResult Function(AppsReceived value)? appsReceived,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $MenuWatchEventCopyWith<$Res> {
+  factory $MenuWatchEventCopyWith(
+          MenuWatchEvent value, $Res Function(MenuWatchEvent) then) =
+      _$MenuWatchEventCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$MenuWatchEventCopyWithImpl<$Res>
+    implements $MenuWatchEventCopyWith<$Res> {
+  _$MenuWatchEventCopyWithImpl(this._value, this._then);
+
+  final MenuWatchEvent _value;
+  // ignore: unused_field
+  final $Res Function(MenuWatchEvent) _then;
+}
+
+/// @nodoc
+abstract class _$StartedCopyWith<$Res> {
+  factory _$StartedCopyWith(_Started value, $Res Function(_Started) then) =
+      __$StartedCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class __$StartedCopyWithImpl<$Res> extends _$MenuWatchEventCopyWithImpl<$Res>
+    implements _$StartedCopyWith<$Res> {
+  __$StartedCopyWithImpl(_Started _value, $Res Function(_Started) _then)
+      : super(_value, (v) => _then(v as _Started));
+
+  @override
+  _Started get _value => super._value as _Started;
+}
+
+/// @nodoc
+
+class _$_Started implements _Started {
+  const _$_Started();
+
+  @override
+  String toString() {
+    return 'MenuWatchEvent.started()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is _Started);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() started,
+    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
+        appsReceived,
+  }) {
+    return started();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? started,
+    TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
+        appsReceived,
+    required TResult orElse(),
+  }) {
+    if (started != null) {
+      return started();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Started value) started,
+    required TResult Function(AppsReceived value) appsReceived,
+  }) {
+    return started(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Started value)? started,
+    TResult Function(AppsReceived value)? appsReceived,
+    required TResult orElse(),
+  }) {
+    if (started != null) {
+      return started(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _Started implements MenuWatchEvent {
+  const factory _Started() = _$_Started;
+}
+
+/// @nodoc
+abstract class $AppsReceivedCopyWith<$Res> {
+  factory $AppsReceivedCopyWith(
+          AppsReceived value, $Res Function(AppsReceived) then) =
+      _$AppsReceivedCopyWithImpl<$Res>;
+  $Res call({Either<List<App>, WorkspaceError> appsOrFail});
+}
+
+/// @nodoc
+class _$AppsReceivedCopyWithImpl<$Res>
+    extends _$MenuWatchEventCopyWithImpl<$Res>
+    implements $AppsReceivedCopyWith<$Res> {
+  _$AppsReceivedCopyWithImpl(
+      AppsReceived _value, $Res Function(AppsReceived) _then)
+      : super(_value, (v) => _then(v as AppsReceived));
+
+  @override
+  AppsReceived get _value => super._value as AppsReceived;
+
+  @override
+  $Res call({
+    Object? appsOrFail = freezed,
+  }) {
+    return _then(AppsReceived(
+      appsOrFail == freezed
+          ? _value.appsOrFail
+          : appsOrFail // ignore: cast_nullable_to_non_nullable
+              as Either<List<App>, WorkspaceError>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$AppsReceived implements AppsReceived {
+  const _$AppsReceived(this.appsOrFail);
+
+  @override
+  final Either<List<App>, WorkspaceError> appsOrFail;
+
+  @override
+  String toString() {
+    return 'MenuWatchEvent.appsReceived(appsOrFail: $appsOrFail)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is AppsReceived &&
+            (identical(other.appsOrFail, appsOrFail) ||
+                const DeepCollectionEquality()
+                    .equals(other.appsOrFail, appsOrFail)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(appsOrFail);
+
+  @JsonKey(ignore: true)
+  @override
+  $AppsReceivedCopyWith<AppsReceived> get copyWith =>
+      _$AppsReceivedCopyWithImpl<AppsReceived>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() started,
+    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
+        appsReceived,
+  }) {
+    return appsReceived(appsOrFail);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? started,
+    TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
+        appsReceived,
+    required TResult orElse(),
+  }) {
+    if (appsReceived != null) {
+      return appsReceived(appsOrFail);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Started value) started,
+    required TResult Function(AppsReceived value) appsReceived,
+  }) {
+    return appsReceived(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Started value)? started,
+    TResult Function(AppsReceived value)? appsReceived,
+    required TResult orElse(),
+  }) {
+    if (appsReceived != null) {
+      return appsReceived(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class AppsReceived implements MenuWatchEvent {
+  const factory AppsReceived(Either<List<App>, WorkspaceError> appsOrFail) =
+      _$AppsReceived;
+
+  Either<List<App>, WorkspaceError> get appsOrFail =>
+      throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $AppsReceivedCopyWith<AppsReceived> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+class _$MenuWatchStateTearOff {
+  const _$MenuWatchStateTearOff();
+
+  _Initial initial() {
+    return const _Initial();
+  }
+
+  _LoadApps loadApps(List<App> apps) {
+    return _LoadApps(
+      apps,
+    );
+  }
+
+  _LoadFail loadFail(WorkspaceError error) {
+    return _LoadFail(
+      error,
+    );
+  }
+}
+
+/// @nodoc
+const $MenuWatchState = _$MenuWatchStateTearOff();
+
+/// @nodoc
+mixin _$MenuWatchState {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(List<App> apps) loadApps,
+    required TResult Function(WorkspaceError error) loadFail,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(List<App> apps)? loadApps,
+    TResult Function(WorkspaceError error)? loadFail,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(_LoadApps value) loadApps,
+    required TResult Function(_LoadFail value) loadFail,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(_LoadApps value)? loadApps,
+    TResult Function(_LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $MenuWatchStateCopyWith<$Res> {
+  factory $MenuWatchStateCopyWith(
+          MenuWatchState value, $Res Function(MenuWatchState) then) =
+      _$MenuWatchStateCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$MenuWatchStateCopyWithImpl<$Res>
+    implements $MenuWatchStateCopyWith<$Res> {
+  _$MenuWatchStateCopyWithImpl(this._value, this._then);
+
+  final MenuWatchState _value;
+  // ignore: unused_field
+  final $Res Function(MenuWatchState) _then;
+}
+
+/// @nodoc
+abstract class _$InitialCopyWith<$Res> {
+  factory _$InitialCopyWith(_Initial value, $Res Function(_Initial) then) =
+      __$InitialCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class __$InitialCopyWithImpl<$Res> extends _$MenuWatchStateCopyWithImpl<$Res>
+    implements _$InitialCopyWith<$Res> {
+  __$InitialCopyWithImpl(_Initial _value, $Res Function(_Initial) _then)
+      : super(_value, (v) => _then(v as _Initial));
+
+  @override
+  _Initial get _value => super._value as _Initial;
+}
+
+/// @nodoc
+
+class _$_Initial implements _Initial {
+  const _$_Initial();
+
+  @override
+  String toString() {
+    return 'MenuWatchState.initial()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is _Initial);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(List<App> apps) loadApps,
+    required TResult Function(WorkspaceError error) loadFail,
+  }) {
+    return initial();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(List<App> apps)? loadApps,
+    TResult Function(WorkspaceError error)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(_LoadApps value) loadApps,
+    required TResult Function(_LoadFail value) loadFail,
+  }) {
+    return initial(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(_LoadApps value)? loadApps,
+    TResult Function(_LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _Initial implements MenuWatchState {
+  const factory _Initial() = _$_Initial;
+}
+
+/// @nodoc
+abstract class _$LoadAppsCopyWith<$Res> {
+  factory _$LoadAppsCopyWith(_LoadApps value, $Res Function(_LoadApps) then) =
+      __$LoadAppsCopyWithImpl<$Res>;
+  $Res call({List<App> apps});
+}
+
+/// @nodoc
+class __$LoadAppsCopyWithImpl<$Res> extends _$MenuWatchStateCopyWithImpl<$Res>
+    implements _$LoadAppsCopyWith<$Res> {
+  __$LoadAppsCopyWithImpl(_LoadApps _value, $Res Function(_LoadApps) _then)
+      : super(_value, (v) => _then(v as _LoadApps));
+
+  @override
+  _LoadApps get _value => super._value as _LoadApps;
+
+  @override
+  $Res call({
+    Object? apps = freezed,
+  }) {
+    return _then(_LoadApps(
+      apps == freezed
+          ? _value.apps
+          : apps // ignore: cast_nullable_to_non_nullable
+              as List<App>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_LoadApps implements _LoadApps {
+  const _$_LoadApps(this.apps);
+
+  @override
+  final List<App> apps;
+
+  @override
+  String toString() {
+    return 'MenuWatchState.loadApps(apps: $apps)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is _LoadApps &&
+            (identical(other.apps, apps) ||
+                const DeepCollectionEquality().equals(other.apps, apps)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(apps);
+
+  @JsonKey(ignore: true)
+  @override
+  _$LoadAppsCopyWith<_LoadApps> get copyWith =>
+      __$LoadAppsCopyWithImpl<_LoadApps>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(List<App> apps) loadApps,
+    required TResult Function(WorkspaceError error) loadFail,
+  }) {
+    return loadApps(apps);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(List<App> apps)? loadApps,
+    TResult Function(WorkspaceError error)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadApps != null) {
+      return loadApps(apps);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(_LoadApps value) loadApps,
+    required TResult Function(_LoadFail value) loadFail,
+  }) {
+    return loadApps(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(_LoadApps value)? loadApps,
+    TResult Function(_LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadApps != null) {
+      return loadApps(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _LoadApps implements MenuWatchState {
+  const factory _LoadApps(List<App> apps) = _$_LoadApps;
+
+  List<App> get apps => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  _$LoadAppsCopyWith<_LoadApps> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class _$LoadFailCopyWith<$Res> {
+  factory _$LoadFailCopyWith(_LoadFail value, $Res Function(_LoadFail) then) =
+      __$LoadFailCopyWithImpl<$Res>;
+  $Res call({WorkspaceError error});
+}
+
+/// @nodoc
+class __$LoadFailCopyWithImpl<$Res> extends _$MenuWatchStateCopyWithImpl<$Res>
+    implements _$LoadFailCopyWith<$Res> {
+  __$LoadFailCopyWithImpl(_LoadFail _value, $Res Function(_LoadFail) _then)
+      : super(_value, (v) => _then(v as _LoadFail));
+
+  @override
+  _LoadFail get _value => super._value as _LoadFail;
+
+  @override
+  $Res call({
+    Object? error = freezed,
+  }) {
+    return _then(_LoadFail(
+      error == freezed
+          ? _value.error
+          : error // ignore: cast_nullable_to_non_nullable
+              as WorkspaceError,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_LoadFail implements _LoadFail {
+  const _$_LoadFail(this.error);
+
+  @override
+  final WorkspaceError error;
+
+  @override
+  String toString() {
+    return 'MenuWatchState.loadFail(error: $error)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is _LoadFail &&
+            (identical(other.error, error) ||
+                const DeepCollectionEquality().equals(other.error, error)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(error);
+
+  @JsonKey(ignore: true)
+  @override
+  _$LoadFailCopyWith<_LoadFail> get copyWith =>
+      __$LoadFailCopyWithImpl<_LoadFail>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(List<App> apps) loadApps,
+    required TResult Function(WorkspaceError error) loadFail,
+  }) {
+    return loadFail(error);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(List<App> apps)? loadApps,
+    TResult Function(WorkspaceError error)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadFail != null) {
+      return loadFail(error);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(_LoadApps value) loadApps,
+    required TResult Function(_LoadFail value) loadFail,
+  }) {
+    return loadFail(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(_LoadApps value)? loadApps,
+    TResult Function(_LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadFail != null) {
+      return loadFail(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _LoadFail implements MenuWatchState {
+  const factory _LoadFail(WorkspaceError error) = _$_LoadFail;
+
+  WorkspaceError get error => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  _$LoadFailCopyWith<_LoadFail> get copyWith =>
+      throw _privateConstructorUsedError;
+}

+ 42 - 0
app_flowy/lib/workspace/application/view/doc_watch_bloc.dart

@@ -0,0 +1,42 @@
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:app_flowy/workspace/domain/i_doc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:flowy_sdk/protobuf/flowy-editor/errors.pb.dart';
+part 'doc_watch_bloc.freezed.dart';
+
+class DocWatchBloc extends Bloc<DocWatchEvent, DocWatchState> {
+  final IDoc iDocImpl;
+
+  DocWatchBloc({
+    required this.iDocImpl,
+  }) : super(const DocWatchState.loading());
+
+  @override
+  Stream<DocWatchState> mapEventToState(DocWatchEvent event) async* {
+    yield* event.map(
+      started: (_) async* {
+        yield* _readDoc();
+      },
+    );
+  }
+
+  Stream<DocWatchState> _readDoc() async* {
+    final docOrFail = await iDocImpl.readDoc();
+    yield docOrFail.fold(
+      (doc) => DocWatchState.loadDoc(doc),
+      (error) => DocWatchState.loadFail(error),
+    );
+  }
+}
+
+@freezed
+class DocWatchEvent with _$DocWatchEvent {
+  const factory DocWatchEvent.started() = Started;
+}
+
+@freezed
+class DocWatchState with _$DocWatchState {
+  const factory DocWatchState.loading() = Loading;
+  const factory DocWatchState.loadDoc(Doc doc) = LoadDoc;
+  const factory DocWatchState.loadFail(EditorError error) = LoadFail;
+}

+ 542 - 0
app_flowy/lib/workspace/application/view/doc_watch_bloc.freezed.dart

@@ -0,0 +1,542 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
+
+part of 'doc_watch_bloc.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+class _$DocWatchEventTearOff {
+  const _$DocWatchEventTearOff();
+
+  Started started() {
+    return const Started();
+  }
+}
+
+/// @nodoc
+const $DocWatchEvent = _$DocWatchEventTearOff();
+
+/// @nodoc
+mixin _$DocWatchEvent {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() started,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? started,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Started value) started,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Started value)? started,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $DocWatchEventCopyWith<$Res> {
+  factory $DocWatchEventCopyWith(
+          DocWatchEvent value, $Res Function(DocWatchEvent) then) =
+      _$DocWatchEventCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$DocWatchEventCopyWithImpl<$Res>
+    implements $DocWatchEventCopyWith<$Res> {
+  _$DocWatchEventCopyWithImpl(this._value, this._then);
+
+  final DocWatchEvent _value;
+  // ignore: unused_field
+  final $Res Function(DocWatchEvent) _then;
+}
+
+/// @nodoc
+abstract class $StartedCopyWith<$Res> {
+  factory $StartedCopyWith(Started value, $Res Function(Started) then) =
+      _$StartedCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$StartedCopyWithImpl<$Res> extends _$DocWatchEventCopyWithImpl<$Res>
+    implements $StartedCopyWith<$Res> {
+  _$StartedCopyWithImpl(Started _value, $Res Function(Started) _then)
+      : super(_value, (v) => _then(v as Started));
+
+  @override
+  Started get _value => super._value as Started;
+}
+
+/// @nodoc
+
+class _$Started implements Started {
+  const _$Started();
+
+  @override
+  String toString() {
+    return 'DocWatchEvent.started()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is Started);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() started,
+  }) {
+    return started();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? started,
+    required TResult orElse(),
+  }) {
+    if (started != null) {
+      return started();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Started value) started,
+  }) {
+    return started(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Started value)? started,
+    required TResult orElse(),
+  }) {
+    if (started != null) {
+      return started(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class Started implements DocWatchEvent {
+  const factory Started() = _$Started;
+}
+
+/// @nodoc
+class _$DocWatchStateTearOff {
+  const _$DocWatchStateTearOff();
+
+  Loading loading() {
+    return const Loading();
+  }
+
+  LoadDoc loadDoc(Doc doc) {
+    return LoadDoc(
+      doc,
+    );
+  }
+
+  LoadFail loadFail(EditorError error) {
+    return LoadFail(
+      error,
+    );
+  }
+}
+
+/// @nodoc
+const $DocWatchState = _$DocWatchStateTearOff();
+
+/// @nodoc
+mixin _$DocWatchState {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() loading,
+    required TResult Function(Doc doc) loadDoc,
+    required TResult Function(EditorError error) loadFail,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? loading,
+    TResult Function(Doc doc)? loadDoc,
+    TResult Function(EditorError error)? loadFail,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Loading value) loading,
+    required TResult Function(LoadDoc value) loadDoc,
+    required TResult Function(LoadFail value) loadFail,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Loading value)? loading,
+    TResult Function(LoadDoc value)? loadDoc,
+    TResult Function(LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $DocWatchStateCopyWith<$Res> {
+  factory $DocWatchStateCopyWith(
+          DocWatchState value, $Res Function(DocWatchState) then) =
+      _$DocWatchStateCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$DocWatchStateCopyWithImpl<$Res>
+    implements $DocWatchStateCopyWith<$Res> {
+  _$DocWatchStateCopyWithImpl(this._value, this._then);
+
+  final DocWatchState _value;
+  // ignore: unused_field
+  final $Res Function(DocWatchState) _then;
+}
+
+/// @nodoc
+abstract class $LoadingCopyWith<$Res> {
+  factory $LoadingCopyWith(Loading value, $Res Function(Loading) then) =
+      _$LoadingCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$LoadingCopyWithImpl<$Res> extends _$DocWatchStateCopyWithImpl<$Res>
+    implements $LoadingCopyWith<$Res> {
+  _$LoadingCopyWithImpl(Loading _value, $Res Function(Loading) _then)
+      : super(_value, (v) => _then(v as Loading));
+
+  @override
+  Loading get _value => super._value as Loading;
+}
+
+/// @nodoc
+
+class _$Loading implements Loading {
+  const _$Loading();
+
+  @override
+  String toString() {
+    return 'DocWatchState.loading()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is Loading);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() loading,
+    required TResult Function(Doc doc) loadDoc,
+    required TResult Function(EditorError error) loadFail,
+  }) {
+    return loading();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? loading,
+    TResult Function(Doc doc)? loadDoc,
+    TResult Function(EditorError error)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loading != null) {
+      return loading();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Loading value) loading,
+    required TResult Function(LoadDoc value) loadDoc,
+    required TResult Function(LoadFail value) loadFail,
+  }) {
+    return loading(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Loading value)? loading,
+    TResult Function(LoadDoc value)? loadDoc,
+    TResult Function(LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loading != null) {
+      return loading(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class Loading implements DocWatchState {
+  const factory Loading() = _$Loading;
+}
+
+/// @nodoc
+abstract class $LoadDocCopyWith<$Res> {
+  factory $LoadDocCopyWith(LoadDoc value, $Res Function(LoadDoc) then) =
+      _$LoadDocCopyWithImpl<$Res>;
+  $Res call({Doc doc});
+}
+
+/// @nodoc
+class _$LoadDocCopyWithImpl<$Res> extends _$DocWatchStateCopyWithImpl<$Res>
+    implements $LoadDocCopyWith<$Res> {
+  _$LoadDocCopyWithImpl(LoadDoc _value, $Res Function(LoadDoc) _then)
+      : super(_value, (v) => _then(v as LoadDoc));
+
+  @override
+  LoadDoc get _value => super._value as LoadDoc;
+
+  @override
+  $Res call({
+    Object? doc = freezed,
+  }) {
+    return _then(LoadDoc(
+      doc == freezed
+          ? _value.doc
+          : doc // ignore: cast_nullable_to_non_nullable
+              as Doc,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$LoadDoc implements LoadDoc {
+  const _$LoadDoc(this.doc);
+
+  @override
+  final Doc doc;
+
+  @override
+  String toString() {
+    return 'DocWatchState.loadDoc(doc: $doc)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is LoadDoc &&
+            (identical(other.doc, doc) ||
+                const DeepCollectionEquality().equals(other.doc, doc)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(doc);
+
+  @JsonKey(ignore: true)
+  @override
+  $LoadDocCopyWith<LoadDoc> get copyWith =>
+      _$LoadDocCopyWithImpl<LoadDoc>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() loading,
+    required TResult Function(Doc doc) loadDoc,
+    required TResult Function(EditorError error) loadFail,
+  }) {
+    return loadDoc(doc);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? loading,
+    TResult Function(Doc doc)? loadDoc,
+    TResult Function(EditorError error)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadDoc != null) {
+      return loadDoc(doc);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Loading value) loading,
+    required TResult Function(LoadDoc value) loadDoc,
+    required TResult Function(LoadFail value) loadFail,
+  }) {
+    return loadDoc(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Loading value)? loading,
+    TResult Function(LoadDoc value)? loadDoc,
+    TResult Function(LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadDoc != null) {
+      return loadDoc(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class LoadDoc implements DocWatchState {
+  const factory LoadDoc(Doc doc) = _$LoadDoc;
+
+  Doc get doc => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $LoadDocCopyWith<LoadDoc> get copyWith => throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $LoadFailCopyWith<$Res> {
+  factory $LoadFailCopyWith(LoadFail value, $Res Function(LoadFail) then) =
+      _$LoadFailCopyWithImpl<$Res>;
+  $Res call({EditorError error});
+}
+
+/// @nodoc
+class _$LoadFailCopyWithImpl<$Res> extends _$DocWatchStateCopyWithImpl<$Res>
+    implements $LoadFailCopyWith<$Res> {
+  _$LoadFailCopyWithImpl(LoadFail _value, $Res Function(LoadFail) _then)
+      : super(_value, (v) => _then(v as LoadFail));
+
+  @override
+  LoadFail get _value => super._value as LoadFail;
+
+  @override
+  $Res call({
+    Object? error = freezed,
+  }) {
+    return _then(LoadFail(
+      error == freezed
+          ? _value.error
+          : error // ignore: cast_nullable_to_non_nullable
+              as EditorError,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$LoadFail implements LoadFail {
+  const _$LoadFail(this.error);
+
+  @override
+  final EditorError error;
+
+  @override
+  String toString() {
+    return 'DocWatchState.loadFail(error: $error)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is LoadFail &&
+            (identical(other.error, error) ||
+                const DeepCollectionEquality().equals(other.error, error)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(error);
+
+  @JsonKey(ignore: true)
+  @override
+  $LoadFailCopyWith<LoadFail> get copyWith =>
+      _$LoadFailCopyWithImpl<LoadFail>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() loading,
+    required TResult Function(Doc doc) loadDoc,
+    required TResult Function(EditorError error) loadFail,
+  }) {
+    return loadFail(error);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? loading,
+    TResult Function(Doc doc)? loadDoc,
+    TResult Function(EditorError error)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadFail != null) {
+      return loadFail(error);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Loading value) loading,
+    required TResult Function(LoadDoc value) loadDoc,
+    required TResult Function(LoadFail value) loadFail,
+  }) {
+    return loadFail(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Loading value)? loading,
+    TResult Function(LoadDoc value)? loadDoc,
+    TResult Function(LoadFail value)? loadFail,
+    required TResult orElse(),
+  }) {
+    if (loadFail != null) {
+      return loadFail(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class LoadFail implements DocWatchState {
+  const factory LoadFail(EditorError error) = _$LoadFail;
+
+  EditorError get error => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $LoadFailCopyWith<LoadFail> get copyWith =>
+      throw _privateConstructorUsedError;
+}

+ 44 - 0
app_flowy/lib/workspace/application/view/view_bloc.dart

@@ -0,0 +1,44 @@
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+import 'package:app_flowy/workspace/domain/i_view.dart';
+
+part 'view_bloc.freezed.dart';
+
+class ViewBloc extends Bloc<ViewEvent, ViewState> {
+  final IView iViewImpl;
+
+  ViewBloc({
+    required this.iViewImpl,
+  }) : super(ViewState.initial());
+
+  @override
+  Stream<ViewState> mapEventToState(ViewEvent event) async* {
+    yield* event.map(initial: (_) async* {
+      yield state;
+    });
+  }
+}
+
+@freezed
+abstract class ViewEvent with _$ViewEvent {
+  const factory ViewEvent.initial() = Initial;
+}
+
+@freezed
+abstract class ViewState implements _$ViewState {
+  const factory ViewState({
+    required bool isLoading,
+    required Option<View> view,
+    required Either<Unit, WorkspaceError> successOrFailure,
+  }) = _ViewState;
+
+  factory ViewState.initial() => ViewState(
+        isLoading: false,
+        view: none(),
+        successOrFailure: left(unit),
+      );
+}

+ 327 - 0
app_flowy/lib/workspace/application/view/view_bloc.freezed.dart

@@ -0,0 +1,327 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
+
+part of 'view_bloc.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+class _$ViewEventTearOff {
+  const _$ViewEventTearOff();
+
+  Initial initial() {
+    return const Initial();
+  }
+}
+
+/// @nodoc
+const $ViewEvent = _$ViewEventTearOff();
+
+/// @nodoc
+mixin _$ViewEvent {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $ViewEventCopyWith<$Res> {
+  factory $ViewEventCopyWith(ViewEvent value, $Res Function(ViewEvent) then) =
+      _$ViewEventCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$ViewEventCopyWithImpl<$Res> implements $ViewEventCopyWith<$Res> {
+  _$ViewEventCopyWithImpl(this._value, this._then);
+
+  final ViewEvent _value;
+  // ignore: unused_field
+  final $Res Function(ViewEvent) _then;
+}
+
+/// @nodoc
+abstract class $InitialCopyWith<$Res> {
+  factory $InitialCopyWith(Initial value, $Res Function(Initial) then) =
+      _$InitialCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$InitialCopyWithImpl<$Res> extends _$ViewEventCopyWithImpl<$Res>
+    implements $InitialCopyWith<$Res> {
+  _$InitialCopyWithImpl(Initial _value, $Res Function(Initial) _then)
+      : super(_value, (v) => _then(v as Initial));
+
+  @override
+  Initial get _value => super._value as Initial;
+}
+
+/// @nodoc
+
+class _$Initial implements Initial {
+  const _$Initial();
+
+  @override
+  String toString() {
+    return 'ViewEvent.initial()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is Initial);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+  }) {
+    return initial();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+  }) {
+    return initial(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class Initial implements ViewEvent {
+  const factory Initial() = _$Initial;
+}
+
+/// @nodoc
+class _$ViewStateTearOff {
+  const _$ViewStateTearOff();
+
+  _ViewState call(
+      {required bool isLoading,
+      required Option<View> view,
+      required Either<Unit, WorkspaceError> successOrFailure}) {
+    return _ViewState(
+      isLoading: isLoading,
+      view: view,
+      successOrFailure: successOrFailure,
+    );
+  }
+}
+
+/// @nodoc
+const $ViewState = _$ViewStateTearOff();
+
+/// @nodoc
+mixin _$ViewState {
+  bool get isLoading => throw _privateConstructorUsedError;
+  Option<View> get view => throw _privateConstructorUsedError;
+  Either<Unit, WorkspaceError> get successOrFailure =>
+      throw _privateConstructorUsedError;
+
+  @JsonKey(ignore: true)
+  $ViewStateCopyWith<ViewState> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $ViewStateCopyWith<$Res> {
+  factory $ViewStateCopyWith(ViewState value, $Res Function(ViewState) then) =
+      _$ViewStateCopyWithImpl<$Res>;
+  $Res call(
+      {bool isLoading,
+      Option<View> view,
+      Either<Unit, WorkspaceError> successOrFailure});
+}
+
+/// @nodoc
+class _$ViewStateCopyWithImpl<$Res> implements $ViewStateCopyWith<$Res> {
+  _$ViewStateCopyWithImpl(this._value, this._then);
+
+  final ViewState _value;
+  // ignore: unused_field
+  final $Res Function(ViewState) _then;
+
+  @override
+  $Res call({
+    Object? isLoading = freezed,
+    Object? view = freezed,
+    Object? successOrFailure = freezed,
+  }) {
+    return _then(_value.copyWith(
+      isLoading: isLoading == freezed
+          ? _value.isLoading
+          : isLoading // ignore: cast_nullable_to_non_nullable
+              as bool,
+      view: view == freezed
+          ? _value.view
+          : view // ignore: cast_nullable_to_non_nullable
+              as Option<View>,
+      successOrFailure: successOrFailure == freezed
+          ? _value.successOrFailure
+          : successOrFailure // ignore: cast_nullable_to_non_nullable
+              as Either<Unit, WorkspaceError>,
+    ));
+  }
+}
+
+/// @nodoc
+abstract class _$ViewStateCopyWith<$Res> implements $ViewStateCopyWith<$Res> {
+  factory _$ViewStateCopyWith(
+          _ViewState value, $Res Function(_ViewState) then) =
+      __$ViewStateCopyWithImpl<$Res>;
+  @override
+  $Res call(
+      {bool isLoading,
+      Option<View> view,
+      Either<Unit, WorkspaceError> successOrFailure});
+}
+
+/// @nodoc
+class __$ViewStateCopyWithImpl<$Res> extends _$ViewStateCopyWithImpl<$Res>
+    implements _$ViewStateCopyWith<$Res> {
+  __$ViewStateCopyWithImpl(_ViewState _value, $Res Function(_ViewState) _then)
+      : super(_value, (v) => _then(v as _ViewState));
+
+  @override
+  _ViewState get _value => super._value as _ViewState;
+
+  @override
+  $Res call({
+    Object? isLoading = freezed,
+    Object? view = freezed,
+    Object? successOrFailure = freezed,
+  }) {
+    return _then(_ViewState(
+      isLoading: isLoading == freezed
+          ? _value.isLoading
+          : isLoading // ignore: cast_nullable_to_non_nullable
+              as bool,
+      view: view == freezed
+          ? _value.view
+          : view // ignore: cast_nullable_to_non_nullable
+              as Option<View>,
+      successOrFailure: successOrFailure == freezed
+          ? _value.successOrFailure
+          : successOrFailure // ignore: cast_nullable_to_non_nullable
+              as Either<Unit, WorkspaceError>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_ViewState implements _ViewState {
+  const _$_ViewState(
+      {required this.isLoading,
+      required this.view,
+      required this.successOrFailure});
+
+  @override
+  final bool isLoading;
+  @override
+  final Option<View> view;
+  @override
+  final Either<Unit, WorkspaceError> successOrFailure;
+
+  @override
+  String toString() {
+    return 'ViewState(isLoading: $isLoading, view: $view, successOrFailure: $successOrFailure)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is _ViewState &&
+            (identical(other.isLoading, isLoading) ||
+                const DeepCollectionEquality()
+                    .equals(other.isLoading, isLoading)) &&
+            (identical(other.view, view) ||
+                const DeepCollectionEquality().equals(other.view, view)) &&
+            (identical(other.successOrFailure, successOrFailure) ||
+                const DeepCollectionEquality()
+                    .equals(other.successOrFailure, successOrFailure)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^
+      const DeepCollectionEquality().hash(isLoading) ^
+      const DeepCollectionEquality().hash(view) ^
+      const DeepCollectionEquality().hash(successOrFailure);
+
+  @JsonKey(ignore: true)
+  @override
+  _$ViewStateCopyWith<_ViewState> get copyWith =>
+      __$ViewStateCopyWithImpl<_ViewState>(this, _$identity);
+}
+
+abstract class _ViewState implements ViewState {
+  const factory _ViewState(
+      {required bool isLoading,
+      required Option<View> view,
+      required Either<Unit, WorkspaceError> successOrFailure}) = _$_ViewState;
+
+  @override
+  bool get isLoading => throw _privateConstructorUsedError;
+  @override
+  Option<View> get view => throw _privateConstructorUsedError;
+  @override
+  Either<Unit, WorkspaceError> get successOrFailure =>
+      throw _privateConstructorUsedError;
+  @override
+  @JsonKey(ignore: true)
+  _$ViewStateCopyWith<_ViewState> get copyWith =>
+      throw _privateConstructorUsedError;
+}

+ 0 - 0
app_flowy/lib/home/domain/edit_context.dart → app_flowy/lib/workspace/domain/edit_context.dart


+ 21 - 0
app_flowy/lib/workspace/domain/i_app.dart

@@ -0,0 +1,21 @@
+import 'package:flowy_sdk/protobuf/flowy-workspace/protobuf.dart';
+import 'package:dartz/dartz.dart';
+
+typedef AppUpdatedCallback = void Function(String name, String desc);
+typedef AppAddViewCallback = void Function(
+    Either<List<View>, WorkspaceError> viewsOrFailed);
+
+abstract class IApp {
+  Future<Either<List<View>, WorkspaceError>> getViews();
+
+  Future<Either<View, WorkspaceError>> createView(
+      {required String name, String? desc, required ViewType viewType});
+}
+
+abstract class IAppWatch {
+  void startWatching(
+      {AppAddViewCallback? addViewCallback,
+      AppUpdatedCallback? updatedCallback});
+
+  Future<void> stopWatching();
+}

+ 18 - 0
app_flowy/lib/workspace/domain/i_doc.dart

@@ -0,0 +1,18 @@
+import 'package:flowy_editor/flowy_editor.dart';
+import 'package:flowy_sdk/protobuf/flowy-editor/doc_create.pb.dart';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-editor/errors.pb.dart';
+
+class Doc {
+  final DocInfo info;
+  final Document data;
+
+  Doc({required this.info, required this.data});
+}
+
+abstract class IDoc {
+  Future<Either<Doc, EditorError>> readDoc();
+  Future<Either<Unit, EditorError>> updateDoc(
+      {String? name, String? desc, String? text});
+  Future<Either<Unit, EditorError>> closeDoc();
+}

+ 15 - 0
app_flowy/lib/workspace/domain/i_view.dart

@@ -0,0 +1,15 @@
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:dartz/dartz.dart';
+
+typedef ViewUpdatedCallback = void Function(View view);
+
+abstract class IView {
+  Future<Either<View, WorkspaceError>> readView();
+}
+
+abstract class IViewWatch {
+  void startWatching({ViewUpdatedCallback? updatedCallback});
+
+  Future<void> stopWatching();
+}

+ 21 - 0
app_flowy/lib/workspace/domain/i_workspace.dart

@@ -0,0 +1,21 @@
+import 'package:flowy_sdk/protobuf/flowy-workspace/protobuf.dart';
+import 'package:dartz/dartz.dart';
+
+typedef WorkspaceAddAppCallback = void Function(
+    Either<List<App>, WorkspaceError> appsOrFail);
+typedef WorkspaceUpdatedCallback = void Function(String name, String desc);
+
+abstract class IWorkspace {
+  Future<Either<App, WorkspaceError>> createApp(
+      {required String name, String? desc});
+
+  Future<Either<List<App>, WorkspaceError>> getApps();
+}
+
+abstract class IWorkspaceWatch {
+  void startWatching(
+      {WorkspaceAddAppCallback? addAppCallback,
+      WorkspaceUpdatedCallback? updatedCallback});
+
+  Future<void> stopWatching();
+}

+ 90 - 0
app_flowy/lib/workspace/domain/page_stack/page_stack.dart

@@ -0,0 +1,90 @@
+import 'package:app_flowy/workspace/domain/page_stack/page_stack_bloc.dart';
+import 'package:app_flowy/workspace/presentation/doc/doc_page.dart';
+import 'package:equatable/equatable.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:app_flowy/workspace/presentation/widgets/blank_page.dart';
+import 'package:app_flowy/workspace/presentation/widgets/fading_index_stack.dart';
+import 'package:app_flowy/workspace/presentation/widgets/prelude.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+abstract class HomeStackView extends Equatable {
+  final ViewType type;
+  final String title;
+  const HomeStackView({required this.type, required this.title});
+}
+
+class HomePageStack {
+  final PageStackBloc _bloc = PageStackBloc();
+  HomePageStack();
+
+  String title() {
+    return _bloc.state.stackView.title;
+  }
+
+  void setStackView(HomeStackView? stackView) {
+    _bloc.add(PageStackEvent.setStackView(stackView ?? const BlankStackView()));
+  }
+
+  Widget stackTopBar() {
+    return BlocProvider<PageStackBloc>(
+      create: (context) => _bloc,
+      child: BlocBuilder<PageStackBloc, PageStackState>(
+        builder: (context, state) {
+          return HomeTopBar(
+            title: state.stackView.title,
+          );
+        },
+      ),
+    );
+  }
+
+  Widget stackWidget() {
+    return BlocProvider<PageStackBloc>(
+      create: (context) => _bloc,
+      child: BlocBuilder<PageStackBloc, PageStackState>(
+        builder: (context, state) {
+          return FadingIndexedStack(
+            index: pages.indexOf(state.stackView.type),
+            children: _buildStackWidget(state.stackView),
+          );
+        },
+      ),
+    );
+  }
+}
+
+List<ViewType> pages = ViewType.values.toList();
+
+List<Widget> _buildStackWidget(HomeStackView stackView) {
+  return ViewType.values.map((viewType) {
+    if (viewType == stackView.type) {
+      switch (stackView.type) {
+        case ViewType.Blank:
+          return BlankPage(stackView: stackView as BlankStackView);
+        case ViewType.Doc:
+          return DocPage(stackView: stackView as DocPageStackView);
+        default:
+          return BlankPage(stackView: stackView as BlankStackView);
+      }
+    } else {
+      return const BlankPage(stackView: BlankStackView());
+    }
+  }).toList();
+}
+
+HomeStackView stackViewFromView(View view) {
+  switch (view.viewType) {
+    case ViewType.Blank:
+      return const BlankStackView();
+    case ViewType.Doc:
+      return DocPageStackView(view);
+    default:
+      return const BlankStackView();
+  }
+}
+
+abstract class HomeStackWidget extends StatefulWidget {
+  final HomeStackView stackView;
+  const HomeStackWidget({Key? key, required this.stackView}) : super(key: key);
+}

+ 35 - 0
app_flowy/lib/workspace/domain/page_stack/page_stack_bloc.dart

@@ -0,0 +1,35 @@
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
+import 'package:app_flowy/workspace/presentation/widgets/blank_page.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+part 'page_stack_bloc.freezed.dart';
+
+class PageStackBloc extends Bloc<PageStackEvent, PageStackState> {
+  PageStackBloc() : super(PageStackState.initial());
+
+  @override
+  Stream<PageStackState> mapEventToState(
+    PageStackEvent event,
+  ) async* {
+    yield* event.map(setStackView: (NewPageContext value) async* {
+      yield state.copyWith(stackView: value.newStackView);
+    });
+  }
+}
+
+@freezed
+abstract class PageStackEvent with _$PageStackEvent {
+  const factory PageStackEvent.setStackView(HomeStackView newStackView) =
+      NewPageContext;
+}
+
+@freezed
+abstract class PageStackState implements _$PageStackState {
+  const factory PageStackState({
+    required HomeStackView stackView,
+  }) = _PageStackState;
+
+  factory PageStackState.initial() => const PageStackState(
+        stackView: BlankStackView(),
+      );
+}

+ 337 - 0
app_flowy/lib/workspace/domain/page_stack/page_stack_bloc.freezed.dart

@@ -0,0 +1,337 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
+
+part of 'page_stack_bloc.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+class _$PageStackEventTearOff {
+  const _$PageStackEventTearOff();
+
+  NewPageContext setStackView(HomeStackView newStackView) {
+    return NewPageContext(
+      newStackView,
+    );
+  }
+}
+
+/// @nodoc
+const $PageStackEvent = _$PageStackEventTearOff();
+
+/// @nodoc
+mixin _$PageStackEvent {
+  HomeStackView get newStackView => throw _privateConstructorUsedError;
+
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(HomeStackView newStackView) setStackView,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(HomeStackView newStackView)? setStackView,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NewPageContext value) setStackView,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NewPageContext value)? setStackView,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+
+  @JsonKey(ignore: true)
+  $PageStackEventCopyWith<PageStackEvent> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $PageStackEventCopyWith<$Res> {
+  factory $PageStackEventCopyWith(
+          PageStackEvent value, $Res Function(PageStackEvent) then) =
+      _$PageStackEventCopyWithImpl<$Res>;
+  $Res call({HomeStackView newStackView});
+}
+
+/// @nodoc
+class _$PageStackEventCopyWithImpl<$Res>
+    implements $PageStackEventCopyWith<$Res> {
+  _$PageStackEventCopyWithImpl(this._value, this._then);
+
+  final PageStackEvent _value;
+  // ignore: unused_field
+  final $Res Function(PageStackEvent) _then;
+
+  @override
+  $Res call({
+    Object? newStackView = freezed,
+  }) {
+    return _then(_value.copyWith(
+      newStackView: newStackView == freezed
+          ? _value.newStackView
+          : newStackView // ignore: cast_nullable_to_non_nullable
+              as HomeStackView,
+    ));
+  }
+}
+
+/// @nodoc
+abstract class $NewPageContextCopyWith<$Res>
+    implements $PageStackEventCopyWith<$Res> {
+  factory $NewPageContextCopyWith(
+          NewPageContext value, $Res Function(NewPageContext) then) =
+      _$NewPageContextCopyWithImpl<$Res>;
+  @override
+  $Res call({HomeStackView newStackView});
+}
+
+/// @nodoc
+class _$NewPageContextCopyWithImpl<$Res>
+    extends _$PageStackEventCopyWithImpl<$Res>
+    implements $NewPageContextCopyWith<$Res> {
+  _$NewPageContextCopyWithImpl(
+      NewPageContext _value, $Res Function(NewPageContext) _then)
+      : super(_value, (v) => _then(v as NewPageContext));
+
+  @override
+  NewPageContext get _value => super._value as NewPageContext;
+
+  @override
+  $Res call({
+    Object? newStackView = freezed,
+  }) {
+    return _then(NewPageContext(
+      newStackView == freezed
+          ? _value.newStackView
+          : newStackView // ignore: cast_nullable_to_non_nullable
+              as HomeStackView,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$NewPageContext implements NewPageContext {
+  const _$NewPageContext(this.newStackView);
+
+  @override
+  final HomeStackView newStackView;
+
+  @override
+  String toString() {
+    return 'PageStackEvent.setStackView(newStackView: $newStackView)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is NewPageContext &&
+            (identical(other.newStackView, newStackView) ||
+                const DeepCollectionEquality()
+                    .equals(other.newStackView, newStackView)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(newStackView);
+
+  @JsonKey(ignore: true)
+  @override
+  $NewPageContextCopyWith<NewPageContext> get copyWith =>
+      _$NewPageContextCopyWithImpl<NewPageContext>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(HomeStackView newStackView) setStackView,
+  }) {
+    return setStackView(newStackView);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(HomeStackView newStackView)? setStackView,
+    required TResult orElse(),
+  }) {
+    if (setStackView != null) {
+      return setStackView(newStackView);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(NewPageContext value) setStackView,
+  }) {
+    return setStackView(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(NewPageContext value)? setStackView,
+    required TResult orElse(),
+  }) {
+    if (setStackView != null) {
+      return setStackView(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class NewPageContext implements PageStackEvent {
+  const factory NewPageContext(HomeStackView newStackView) = _$NewPageContext;
+
+  @override
+  HomeStackView get newStackView => throw _privateConstructorUsedError;
+  @override
+  @JsonKey(ignore: true)
+  $NewPageContextCopyWith<NewPageContext> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+class _$PageStackStateTearOff {
+  const _$PageStackStateTearOff();
+
+  _PageStackState call({required HomeStackView stackView}) {
+    return _PageStackState(
+      stackView: stackView,
+    );
+  }
+}
+
+/// @nodoc
+const $PageStackState = _$PageStackStateTearOff();
+
+/// @nodoc
+mixin _$PageStackState {
+  HomeStackView get stackView => throw _privateConstructorUsedError;
+
+  @JsonKey(ignore: true)
+  $PageStackStateCopyWith<PageStackState> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $PageStackStateCopyWith<$Res> {
+  factory $PageStackStateCopyWith(
+          PageStackState value, $Res Function(PageStackState) then) =
+      _$PageStackStateCopyWithImpl<$Res>;
+  $Res call({HomeStackView stackView});
+}
+
+/// @nodoc
+class _$PageStackStateCopyWithImpl<$Res>
+    implements $PageStackStateCopyWith<$Res> {
+  _$PageStackStateCopyWithImpl(this._value, this._then);
+
+  final PageStackState _value;
+  // ignore: unused_field
+  final $Res Function(PageStackState) _then;
+
+  @override
+  $Res call({
+    Object? stackView = freezed,
+  }) {
+    return _then(_value.copyWith(
+      stackView: stackView == freezed
+          ? _value.stackView
+          : stackView // ignore: cast_nullable_to_non_nullable
+              as HomeStackView,
+    ));
+  }
+}
+
+/// @nodoc
+abstract class _$PageStackStateCopyWith<$Res>
+    implements $PageStackStateCopyWith<$Res> {
+  factory _$PageStackStateCopyWith(
+          _PageStackState value, $Res Function(_PageStackState) then) =
+      __$PageStackStateCopyWithImpl<$Res>;
+  @override
+  $Res call({HomeStackView stackView});
+}
+
+/// @nodoc
+class __$PageStackStateCopyWithImpl<$Res>
+    extends _$PageStackStateCopyWithImpl<$Res>
+    implements _$PageStackStateCopyWith<$Res> {
+  __$PageStackStateCopyWithImpl(
+      _PageStackState _value, $Res Function(_PageStackState) _then)
+      : super(_value, (v) => _then(v as _PageStackState));
+
+  @override
+  _PageStackState get _value => super._value as _PageStackState;
+
+  @override
+  $Res call({
+    Object? stackView = freezed,
+  }) {
+    return _then(_PageStackState(
+      stackView: stackView == freezed
+          ? _value.stackView
+          : stackView // ignore: cast_nullable_to_non_nullable
+              as HomeStackView,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_PageStackState implements _PageStackState {
+  const _$_PageStackState({required this.stackView});
+
+  @override
+  final HomeStackView stackView;
+
+  @override
+  String toString() {
+    return 'PageStackState(stackView: $stackView)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is _PageStackState &&
+            (identical(other.stackView, stackView) ||
+                const DeepCollectionEquality()
+                    .equals(other.stackView, stackView)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(stackView);
+
+  @JsonKey(ignore: true)
+  @override
+  _$PageStackStateCopyWith<_PageStackState> get copyWith =>
+      __$PageStackStateCopyWithImpl<_PageStackState>(this, _$identity);
+}
+
+abstract class _PageStackState implements PageStackState {
+  const factory _PageStackState({required HomeStackView stackView}) =
+      _$_PageStackState;
+
+  @override
+  HomeStackView get stackView => throw _privateConstructorUsedError;
+  @override
+  @JsonKey(ignore: true)
+  _$PageStackStateCopyWith<_PageStackState> get copyWith =>
+      throw _privateConstructorUsedError;
+}

+ 72 - 0
app_flowy/lib/workspace/infrastructure/deps_resolver.dart

@@ -0,0 +1,72 @@
+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/doc/doc_bloc.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/application/view/doc_watch_bloc.dart';
+import 'package:app_flowy/workspace/application/view/view_bloc.dart';
+import 'package:app_flowy/workspace/domain/i_doc.dart';
+import 'package:app_flowy/workspace/domain/i_view.dart';
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
+import 'package:app_flowy/workspace/infrastructure/i_app_impl.dart';
+import 'package:app_flowy/workspace/infrastructure/i_doc_impl.dart';
+import 'package:app_flowy/workspace/infrastructure/i_workspace_impl.dart';
+import 'package:app_flowy/workspace/infrastructure/repos/app_repo.dart';
+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:get_it/get_it.dart';
+
+import 'i_view_impl.dart';
+
+class HomeDepsResolver {
+  static Future<void> resolve(GetIt getIt) async {
+    //
+    getIt.registerLazySingleton<HomePageStack>(() => HomePageStack());
+
+    //App
+    getIt.registerFactoryParam<IApp, String, void>(
+        (appId, _) => IAppImpl(repo: AppRepository(appId: appId)));
+    getIt.registerFactoryParam<IAppWatch, String, void>(
+        (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)));
+
+    // View
+    getIt.registerFactoryParam<IView, String, void>(
+        (viewId, _) => IViewImpl(repo: ViewRepository(viewId: viewId)));
+    getIt.registerFactoryParam<IViewWatch, String, void>((viewId, _) =>
+        IViewWatchImpl(repo: ViewWatchRepository(viewId: viewId)));
+
+    // Doc
+    getIt.registerFactoryParam<IDoc, String, void>(
+        (docId, _) => IDocImpl(repo: DocRepository(docId: docId)));
+
+    //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<AppBloc, String, void>(
+        (appId, _) => AppBloc(getIt<IApp>(param1: appId)));
+    getIt.registerFactoryParam<AppWatchBloc, String, void>(
+        (appId, _) => AppWatchBloc(getIt<IAppWatch>(param1: appId)));
+
+    getIt.registerFactoryParam<ViewBloc, String, void>(
+        (viewId, _) => ViewBloc(iViewImpl: getIt<IView>(param1: viewId)));
+
+    getIt.registerFactoryParam<DocWatchBloc, String, void>(
+        (docId, _) => DocWatchBloc(iDocImpl: getIt<IDoc>(param1: docId)));
+
+    getIt.registerFactoryParam<DocBloc, String, void>(
+        (docId, _) => DocBloc(getIt<IDoc>(param1: docId)));
+
+    // getIt.registerFactoryParam<ViewBloc, String, void>(
+    //     (viewId, _) => ViewBloc(iViewImpl: getIt<IView>(param1: viewId)));
+  }
+}

+ 65 - 0
app_flowy/lib/workspace/infrastructure/i_app_impl.dart

@@ -0,0 +1,65 @@
+import 'package:app_flowy/workspace/infrastructure/repos/app_repo.dart';
+import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:app_flowy/workspace/domain/i_app.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+export 'package:app_flowy/workspace/domain/i_app.dart';
+
+class IAppImpl extends IApp {
+  AppRepository repo;
+  IAppImpl({
+    required this.repo,
+  });
+
+  @override
+  Future<Either<List<View>, WorkspaceError>> getViews() {
+    return repo.getViews();
+  }
+
+  @override
+  Future<Either<View, WorkspaceError>> createView(
+      {required String name, String? desc, required ViewType viewType}) {
+    return repo.createView(name, desc ?? "", viewType).then((result) {
+      return result.fold(
+        (view) => _createDoc(view),
+        (r) => right(r),
+      );
+    });
+  }
+
+  Future<Either<View, WorkspaceError>> _createDoc(View view) async {
+    switch (view.viewType) {
+      case ViewType.Doc:
+        final docRepo = DocRepository(docId: view.id);
+        final result = await docRepo.createDoc(
+            name: view.name, desc: "", text: "[{\"insert\":\"\\n\"}]");
+        return result.fold((l) => left(view), (r) {
+          return right(
+              WorkspaceError(code: WorkspaceErrorCode.Unknown, msg: r.msg));
+        });
+      default:
+        return left(view);
+    }
+  }
+}
+
+class IAppWatchImpl extends IAppWatch {
+  AppWatchRepository repo;
+  IAppWatchImpl({
+    required this.repo,
+  });
+
+  @override
+  void startWatching(
+      {AppAddViewCallback? addViewCallback,
+      AppUpdatedCallback? updatedCallback}) {
+    repo.startWatching(
+        addViewCallback: addViewCallback, updatedCallback: updatedCallback);
+  }
+
+  @override
+  Future<void> stopWatching() async {
+    await repo.close();
+  }
+}

+ 51 - 0
app_flowy/lib/workspace/infrastructure/i_doc_impl.dart

@@ -0,0 +1,51 @@
+import 'dart:convert';
+
+import 'package:app_flowy/workspace/domain/i_doc.dart';
+import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart';
+import 'package:flowy_editor/flowy_editor.dart';
+import 'package:flowy_sdk/protobuf/flowy-editor/errors.pb.dart';
+import 'package:dartz/dartz.dart';
+
+class IDocImpl extends IDoc {
+  DocRepository repo;
+
+  IDocImpl({required this.repo});
+
+  @override
+  Future<Either<Unit, EditorError>> closeDoc() {
+    return repo.closeDoc();
+  }
+
+  @override
+  Future<Either<Doc, EditorError>> readDoc() async {
+    final docInfoOrFail = await repo.readDoc();
+    return docInfoOrFail.fold(
+      (info) => _loadDocument(info.path).then((result) => result.fold(
+          (document) => left(Doc(info: info, data: document)),
+          (error) => right(error))),
+      (error) => right(error),
+    );
+  }
+
+  @override
+  Future<Either<Unit, EditorError>> updateDoc(
+      {String? name, String? desc, String? text}) {
+    final json = jsonEncode(text ?? "");
+    return repo.updateDoc(name: name, desc: desc, text: json);
+  }
+
+  Future<Either<Document, EditorError>> _loadDocument(String path) {
+    return repo.readDocData(path).then((docDataOrFail) {
+      return docDataOrFail.fold(
+        (docData) => left(_decodeToDocument(docData.text)),
+        (error) => right(error),
+      );
+    });
+  }
+
+  Document _decodeToDocument(String text) {
+    final json = jsonDecode(text);
+    final document = Document.fromJson(json);
+    return document;
+  }
+}

+ 33 - 0
app_flowy/lib/workspace/infrastructure/i_view_impl.dart

@@ -0,0 +1,33 @@
+import 'package:app_flowy/workspace/domain/i_view.dart';
+import 'package:app_flowy/workspace/infrastructure/repos/view_repo.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:dartz/dartz.dart';
+
+class IViewImpl extends IView {
+  ViewRepository repo;
+
+  IViewImpl({required this.repo});
+
+  @override
+  Future<Either<View, WorkspaceError>> readView() {
+    return repo.readView();
+  }
+}
+
+class IViewWatchImpl extends IViewWatch {
+  final ViewWatchRepository repo;
+  IViewWatchImpl({
+    required this.repo,
+  });
+
+  @override
+  void startWatching({ViewUpdatedCallback? updatedCallback}) {
+    repo.startWatching(updatedCallback: updatedCallback);
+  }
+
+  @override
+  Future<void> stopWatching() async {
+    await repo.close();
+  }
+}

+ 50 - 0
app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart

@@ -0,0 +1,50 @@
+import 'package:app_flowy/workspace/domain/i_workspace.dart';
+import 'package:app_flowy/workspace/infrastructure/repos/workspace_repo.dart';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+
+export 'package:app_flowy/workspace/domain/i_workspace.dart';
+
+class IWorkspaceImpl extends IWorkspace {
+  WorkspaceRepo repo;
+  IWorkspaceImpl({
+    required this.repo,
+  });
+
+  @override
+  Future<Either<App, WorkspaceError>> createApp(
+      {required String name, String? desc}) {
+    return repo.createApp(name, desc ?? "");
+  }
+
+  @override
+  Future<Either<List<App>, WorkspaceError>> getApps() {
+    return repo.getWorkspace(readApps: true).then((result) {
+      return result.fold(
+        (workspace) => left(workspace.apps.items),
+        (error) => right(error),
+      );
+    });
+  }
+}
+
+class IWorkspaceWatchImpl extends IWorkspaceWatch {
+  WorkspaceWatchRepo repo;
+  IWorkspaceWatchImpl({
+    required this.repo,
+  });
+
+  @override
+  void startWatching(
+      {WorkspaceAddAppCallback? addAppCallback,
+      WorkspaceUpdatedCallback? updatedCallback}) {
+    repo.startWatching(
+        addAppCallback: addAppCallback, updatedCallback: updatedCallback);
+  }
+
+  @override
+  Future<void> stopWatching() async {
+    await repo.close();
+  }
+}

+ 115 - 0
app_flowy/lib/workspace/infrastructure/repos/app_repo.dart

@@ -0,0 +1,115 @@
+import 'dart:async';
+import 'package:app_flowy/workspace/domain/i_app.dart';
+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-workspace/app_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/app_query.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/view_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pbenum.dart';
+import 'package:flowy_sdk/rust_stream.dart';
+
+class AppRepository {
+  String appId;
+  AppRepository({
+    required this.appId,
+  });
+
+  Future<Either<App, WorkspaceError>> getAppDesc() {
+    final request = QueryAppRequest.create()
+      ..appId = appId
+      ..readViews = false;
+
+    return WorkspaceEventGetApp(request).send();
+  }
+
+  Future<Either<View, WorkspaceError>> createView(
+      String name, String desc, ViewType viewType) {
+    final request = CreateViewRequest.create()
+      ..appId = appId
+      ..name = name
+      ..desc = desc
+      ..viewType = viewType;
+
+    return WorkspaceEventCreateView(request).send();
+  }
+
+  Future<Either<List<View>, WorkspaceError>> getViews() {
+    final request = QueryAppRequest.create()
+      ..appId = appId
+      ..readViews = true;
+
+    return WorkspaceEventGetApp(request).send().then((result) {
+      return result.fold(
+        (app) => left(app.views.items),
+        (error) => right(error),
+      );
+    });
+  }
+}
+
+class AppWatchRepository {
+  StreamSubscription<ObservableSubject>? _subscription;
+  AppAddViewCallback? _addViewCallback;
+  AppUpdatedCallback? _updatedCallback;
+  String appId;
+  late AppRepository _repo;
+  AppWatchRepository({
+    required this.appId,
+  }) {
+    _repo = AppRepository(appId: appId);
+  }
+
+  void startWatching(
+      {AppAddViewCallback? addViewCallback,
+      AppUpdatedCallback? updatedCallback}) {
+    _addViewCallback = addViewCallback;
+    _updatedCallback = updatedCallback;
+    _subscription = RustStreamReceiver.listen((observable) {
+      if (observable.subjectId != appId) {
+        return;
+      }
+
+      final ty = WorkspaceObservable.valueOf(observable.ty);
+      if (ty != null) {
+        _handleObservableType(ty);
+      }
+    });
+  }
+
+  void _handleObservableType(WorkspaceObservable ty) {
+    switch (ty) {
+      case WorkspaceObservable.AppAddView:
+        if (_addViewCallback == null) {
+          return;
+        }
+        _repo.getViews().then((result) {
+          result.fold(
+            (views) => _addViewCallback!(left(views)),
+            (error) => _addViewCallback!(right(error)),
+          );
+        });
+        break;
+      case WorkspaceObservable.AppUpdateDesc:
+        if (_updatedCallback == null) {
+          return;
+        }
+        _repo.getAppDesc().then((result) {
+          result.fold(
+            (app) => _updatedCallback!(app.name, app.desc),
+            (error) => Log.error(error),
+          );
+        });
+        break;
+      default:
+        break;
+    }
+  }
+
+  Future<void> close() async {
+    await _subscription?.cancel();
+  }
+}

+ 45 - 0
app_flowy/lib/workspace/infrastructure/repos/doc_repo.dart

@@ -0,0 +1,45 @@
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/dispatch/dispatch.dart';
+import 'package:flowy_sdk/protobuf/flowy-editor/doc_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-editor/doc_modify.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-editor/doc_query.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-editor/errors.pb.dart';
+
+class DocRepository {
+  final String docId;
+  DocRepository({
+    required this.docId,
+  });
+
+  Future<Either<DocInfo, EditorError>> createDoc(
+      {required String name, String? desc, String? text}) {
+    final request =
+        CreateDocRequest(id: docId, name: name, desc: desc, text: text);
+
+    return EditorEventCreateDoc(request).send();
+  }
+
+  Future<Either<DocInfo, EditorError>> readDoc() {
+    final request = QueryDocRequest.create()..docId = docId;
+    return EditorEventReadDocInfo(request).send();
+  }
+
+  Future<Either<DocData, EditorError>> readDocData(String path) {
+    final request = QueryDocDataRequest.create()
+      ..docId = docId
+      ..path = path;
+    return EditorEventReadDocData(request).send();
+  }
+
+  Future<Either<Unit, EditorError>> updateDoc(
+      {String? name, String? desc, String? text}) {
+    final request = UpdateDocRequest(id: docId, name: name, text: text);
+
+    return EditorEventUpdateDoc(request).send();
+  }
+
+  Future<Either<Unit, EditorError>> closeDoc(
+      {String? name, String? desc, String? text}) {
+    throw UnimplementedError();
+  }
+}

+ 75 - 0
app_flowy/lib/workspace/infrastructure/repos/view_repo.dart

@@ -0,0 +1,75 @@
+import 'dart:async';
+
+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-workspace/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/observable.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_query.pb.dart';
+import 'package:flowy_sdk/rust_stream.dart';
+
+import 'package:app_flowy/workspace/domain/i_view.dart';
+
+class ViewRepository {
+  String viewId;
+  ViewRepository({
+    required this.viewId,
+  });
+
+  Future<Either<View, WorkspaceError>> readView() {
+    final request = QueryViewRequest.create()..viewId = viewId;
+    return WorkspaceEventReadView(request).send();
+  }
+}
+
+class ViewWatchRepository {
+  StreamSubscription<ObservableSubject>? _subscription;
+  ViewUpdatedCallback? _updatedCallback;
+  String viewId;
+  late ViewRepository _repo;
+  ViewWatchRepository({
+    required this.viewId,
+  }) {
+    _repo = ViewRepository(viewId: viewId);
+  }
+
+  void startWatching({
+    ViewUpdatedCallback? updatedCallback,
+  }) {
+    _updatedCallback = updatedCallback;
+    _subscription = RustStreamReceiver.listen((observable) {
+      if (observable.subjectId != viewId) {
+        return;
+      }
+
+      final ty = WorkspaceObservable.valueOf(observable.ty);
+      if (ty != null) {
+        _handleObservableType(ty);
+      }
+    });
+  }
+
+  void _handleObservableType(WorkspaceObservable ty) {
+    switch (ty) {
+      case WorkspaceObservable.ViewUpdateDesc:
+        if (_updatedCallback == null) {
+          return;
+        }
+        _repo.readView().then((result) {
+          result.fold(
+            (view) => _updatedCallback!(view),
+            (error) => Log.error(error),
+          );
+        });
+        break;
+      default:
+        break;
+    }
+  }
+
+  Future<void> close() async {
+    await _subscription?.cancel();
+  }
+}

+ 116 - 0
app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart

@@ -0,0 +1,116 @@
+import 'dart:async';
+
+import 'package:app_flowy/workspace/domain/i_workspace.dart';
+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-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';
+import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_query.pb.dart';
+import 'package:flowy_sdk/rust_stream.dart';
+
+class WorkspaceRepo {
+  String workspaceId;
+  WorkspaceRepo({
+    required this.workspaceId,
+  });
+
+  Future<Either<App, WorkspaceError>> createApp(String appName, String desc) {
+    return WorkspaceEventGetCurWorkspace().send().then((result) {
+      return result.fold(
+        (workspace) {
+          final request = CreateAppRequest.create()
+            ..name = appName
+            ..workspaceId = workspace.id
+            ..desc = desc;
+          return WorkspaceEventCreateApp(request).send();
+        },
+        (error) {
+          return right(error);
+        },
+      );
+    });
+  }
+
+  Future<Either<Workspace, WorkspaceError>> getWorkspace(
+      {bool readApps = false}) {
+    final request = QueryWorkspaceRequest.create()
+      ..workspaceId = workspaceId
+      ..readApps = readApps;
+
+    return WorkspaceEventGetWorkspace(request).send().then((result) {
+      return result.fold(
+        (workspace) => left(workspace),
+        (error) => right(error),
+      );
+    });
+  }
+}
+
+class WorkspaceWatchRepo {
+  StreamSubscription<ObservableSubject>? _subscription;
+  WorkspaceAddAppCallback? _addAppCallback;
+  WorkspaceUpdatedCallback? _updatedCallback;
+  final String workspaceId;
+  late WorkspaceRepo _repo;
+
+  WorkspaceWatchRepo({
+    required this.workspaceId,
+  }) {
+    _repo = WorkspaceRepo(workspaceId: workspaceId);
+  }
+
+  void startWatching(
+      {WorkspaceAddAppCallback? addAppCallback,
+      WorkspaceUpdatedCallback? updatedCallback}) {
+    _addAppCallback = addAppCallback;
+    _updatedCallback = updatedCallback;
+
+    _subscription = RustStreamReceiver.listen((observable) {
+      if (observable.subjectId != workspaceId) {
+        return;
+      }
+
+      final ty = WorkspaceObservable.valueOf(observable.ty);
+      if (ty != null) {
+        _handleObservableType(ty);
+      }
+    });
+  }
+
+  void _handleObservableType(WorkspaceObservable ty) {
+    switch (ty) {
+      case WorkspaceObservable.WorkspaceUpdateDesc:
+        if (_updatedCallback == null) {
+          return;
+        }
+        _repo.getWorkspace().then((result) {
+          result.fold(
+            (workspace) => _updatedCallback!(workspace.name, workspace.desc),
+            (error) => Log.error(error),
+          );
+        });
+        break;
+      case WorkspaceObservable.WorkspaceAddApp:
+        if (_addAppCallback == null) {
+          return;
+        }
+        _repo.getWorkspace(readApps: true).then((result) {
+          result.fold(
+            (workspace) => _addAppCallback!(left(workspace.apps.items)),
+            (error) => _addAppCallback!(right(error)),
+          );
+        });
+        break;
+      default:
+        break;
+    }
+  }
+
+  Future<void> close() async {
+    await _subscription?.cancel();
+  }
+}

+ 149 - 0
app_flowy/lib/workspace/presentation/app/app_widget.dart

@@ -0,0 +1,149 @@
+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_size.dart';
+import 'package:app_flowy/startup/startup.dart';
+import 'package:expandable/expandable.dart';
+import 'package:flowy_infra/size.dart';
+import 'package:flowy_infra_ui/widget/error_page.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:dartz/dartz.dart';
+
+class AppWidget extends StatelessWidget {
+  final App app;
+  const AppWidget(this.app, {Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return MultiBlocProvider(
+      providers: [
+        BlocProvider<AppBloc>(create: (context) {
+          final appBloc = getIt<AppBloc>(param1: app.id);
+          appBloc.add(const AppEvent.initial());
+          return appBloc;
+        }),
+        BlocProvider<AppWatchBloc>(create: (context) {
+          final watchBloc = getIt<AppWatchBloc>(param1: app.id);
+          watchBloc.add(const AppWatchEvent.started());
+          return watchBloc;
+        }),
+      ],
+      child: BlocBuilder<AppWatchBloc, AppWatchState>(
+        builder: (context, state) {
+          final child = state.map(
+            initial: (_) => BlocBuilder<AppBloc, AppState>(
+              builder: (context, state) {
+                return ViewList(state.views);
+              },
+            ),
+            loadViews: (s) => ViewList(some(s.views)),
+            loadFail: (s) => FlowyErrorPage(s.error.toString()),
+          );
+
+          return expandableWrapper(context, child);
+        },
+      ),
+    );
+  }
+
+  ExpandableNotifier expandableWrapper(BuildContext context, Widget child) {
+    return ExpandableNotifier(
+      child: ScrollOnExpand(
+        scrollOnExpand: true,
+        scrollOnCollapse: false,
+        child: Card(
+          clipBehavior: Clip.antiAlias,
+          child: Column(
+            children: <Widget>[
+              ExpandablePanel(
+                theme: const ExpandableThemeData(
+                  headerAlignment: ExpandablePanelHeaderAlignment.center,
+                  tapBodyToExpand: false,
+                  tapBodyToCollapse: false,
+                  iconPadding: EdgeInsets.zero,
+                  hasIcon: false,
+                ),
+                header: AppHeader(app),
+                expanded: Padding(
+                  padding: EdgeInsets.only(left: Sizes.iconMed),
+                  child: child,
+                ),
+                collapsed: const SizedBox(),
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}
+
+class AppHeader extends StatelessWidget {
+  final App app;
+  const AppHeader(
+    this.app, {
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      color: Colors.white,
+      child: Padding(
+        padding: EdgeInsets.symmetric(vertical: Insets.m),
+        child: Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            ExpandableIcon(
+              theme: ExpandableThemeData(
+                expandIcon: Icons.arrow_right,
+                collapseIcon: Icons.arrow_drop_down,
+                iconColor: Colors.black,
+                iconSize: HomeMenuSize.collapseIconSize,
+                iconPadding: EdgeInsets.zero,
+                hasIcon: false,
+              ),
+            ),
+            Expanded(
+              child: Text(app.name),
+            ),
+            SizedBox(
+              height: HomeMenuSize.createViewButtonSize,
+              child: createViewPopupMenu(context),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget createViewPopupMenu(BuildContext context) {
+    return PopupMenuButton(
+        iconSize: 24,
+        tooltip: 'create new view',
+        icon: const Icon(Icons.add),
+        padding: EdgeInsets.zero,
+        onSelected: (viewType) => _createView(viewType as ViewType, context),
+        itemBuilder: (context) => menuItemBuilder());
+  }
+
+  List<PopupMenuEntry> menuItemBuilder() {
+    return ViewType.values
+        .where((element) => element != ViewType.Blank)
+        .map((ty) {
+      return PopupMenuItem<ViewType>(
+          value: ty,
+          child: Row(
+            children: <Widget>[Text(ty.name)],
+          ));
+    }).toList();
+  }
+
+  void _createView(ViewType viewType, BuildContext context) {
+    context.read<AppBloc>().add(AppEvent.createView("New view", "", viewType));
+  }
+}

+ 0 - 0
app_flowy/lib/workspace/presentation/app/new_app.dart


+ 45 - 0
app_flowy/lib/workspace/presentation/app/view_list.dart

@@ -0,0 +1,45 @@
+import 'package:app_flowy/workspace/presentation/view/view_widget.dart';
+import 'package:flowy_infra/flowy_logger.dart';
+import 'package:flowy_infra/size.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:dartz/dartz.dart';
+import 'package:styled_widget/styled_widget.dart';
+
+class ViewList extends StatelessWidget {
+  final Option<List<View>> views;
+  const ViewList(this.views, {Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    Log.info('ViewList build');
+    return views.fold(
+      () => const SizedBox(
+        height: 10,
+      ),
+      (views) {
+        return Column(
+          children: buildViewWidgets(views),
+        ).padding(vertical: Insets.sm);
+      },
+    );
+  }
+
+  List<ViewWidget> buildViewWidgets(List<View> views) {
+    var targetViews = views.map((view) {
+      return ViewWidget(
+        icon: const Icon(Icons.file_copy),
+        view: view,
+      );
+    }).toList(growable: true);
+    return targetViews;
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    views.fold(() => {},
+        (views) => properties.add(IterableProperty<View>('views', views)));
+  }
+}

+ 51 - 0
app_flowy/lib/workspace/presentation/doc/doc_page.dart

@@ -0,0 +1,51 @@
+import 'package:app_flowy/startup/startup.dart';
+import 'package:app_flowy/workspace/application/view/doc_watch_bloc.dart';
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
+import 'package:app_flowy/workspace/presentation/doc/editor_widget.dart';
+import 'package:flowy_infra_ui/widget/error_page.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class DocPage extends HomeStackWidget {
+  const DocPage({Key? key, required DocPageStackView stackView})
+      : super(key: key, stackView: stackView);
+
+  @override
+  _DocPageState createState() => _DocPageState();
+}
+
+class _DocPageState extends State<DocPage> {
+  @override
+  Widget build(BuildContext context) {
+    final stackView = widget.stackView as DocPageStackView;
+    return MultiBlocProvider(
+      providers: [
+        BlocProvider<DocWatchBloc>(
+            create: (context) => getIt<DocWatchBloc>(param1: stackView.view.id)
+              ..add(const DocWatchEvent.started())),
+      ],
+      child:
+          BlocBuilder<DocWatchBloc, DocWatchState>(builder: (context, state) {
+        assert(widget.stackView is DocPageStackView);
+        return state.map(
+          loading: (_) => const CircularProgressIndicator.adaptive(),
+          loadDoc: (s) => EditorWdiget(doc: s.doc),
+          loadFail: (s) => FlowyErrorPage(s.error.toString()),
+        );
+      }),
+    );
+  }
+}
+
+class DocPageStackView extends HomeStackView {
+  final View view;
+  DocPageStackView(this.view)
+      : super(
+          type: view.viewType,
+          title: view.name,
+        );
+
+  @override
+  List<Object> get props => [view.id, type];
+}

+ 61 - 0
app_flowy/lib/workspace/presentation/doc/editor_widget.dart

@@ -0,0 +1,61 @@
+import 'dart:io';
+
+import 'package:app_flowy/startup/startup.dart';
+import 'package:app_flowy/workspace/application/doc/doc_bloc.dart';
+import 'package:app_flowy/workspace/domain/i_doc.dart';
+import 'package:flowy_editor/flowy_editor.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class EditorWdiget extends StatelessWidget {
+  final FocusNode _focusNode = FocusNode();
+  late EditorController controller;
+  final Doc doc;
+
+  EditorWdiget({Key? key, required this.doc}) : super(key: key) {
+    controller = EditorController(
+      document: doc.data,
+      selection: const TextSelection.collapsed(offset: 0),
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocProvider(
+      create: (context) => getIt<DocBloc>(param1: doc.info.id),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: [
+          _renderEditor(controller),
+          _renderToolbar(controller),
+        ],
+      ),
+    );
+  }
+
+  Widget _renderEditor(EditorController controller) {
+    final editor = FlowyEditor(
+      controller: controller,
+      focusNode: _focusNode,
+      scrollable: true,
+      autoFocus: false,
+      expands: false,
+      padding: const EdgeInsets.symmetric(horizontal: 8.0),
+      readOnly: false,
+      scrollBottomInset: 0,
+      scrollController: ScrollController(),
+    );
+    return Expanded(child: editor);
+  }
+
+  Widget _renderToolbar(EditorController controller) {
+    return FlowyToolbar.basic(
+      controller: controller,
+      onImageSelectCallback: _onImageSelection,
+    );
+  }
+
+  Future<String> _onImageSelection(File file) {
+    throw UnimplementedError();
+  }
+}

+ 8 - 8
app_flowy/lib/home/presentation/home_layout.dart → app_flowy/lib/workspace/presentation/home/home_layout.dart

@@ -1,4 +1,4 @@
-import 'package:app_flowy/home/application/home_bloc.dart';
+import 'package:app_flowy/workspace/application/home/home_bloc.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/time/duration.dart';
 import 'package:flutter/material.dart';
@@ -17,7 +17,8 @@ class HomeLayout {
   late double homePageROffset;
   late Duration animDuration;
 
-  HomeLayout(BuildContext context, BoxConstraints homeScreenConstraint) {
+  HomeLayout(BuildContext context, BoxConstraints homeScreenConstraint,
+      bool forceCollapse) {
     final homeBlocState = context.read<HomeBloc>().state;
 
     showEditPannel = homeBlocState.editContext.isSome();
@@ -27,12 +28,11 @@ class HomeLayout {
       menuWidth = Sizes.sideBarLg;
     }
 
-    // if (menuBlocState.isCollapse) {
-    //   showMenu = false;
-    // } else {
-    //   showMenu = context.widthPx > PageBreaks.TabletPortrait;
-    // }
-    showMenu = context.widthPx > PageBreaks.tabletPortrait;
+    if (forceCollapse) {
+      showMenu = false;
+    } else {
+      showMenu = context.widthPx > PageBreaks.tabletPortrait;
+    }
 
     homePageLOffset = showMenu ? menuWidth : 0.0;
     animDuration = .35.seconds;

+ 45 - 63
app_flowy/lib/home/presentation/home_screen.dart → app_flowy/lib/workspace/presentation/home/home_screen.dart

@@ -1,7 +1,7 @@
-import 'package:app_flowy/home/application/home_bloc.dart';
-import 'package:app_flowy/home/application/watcher/home_watcher_bloc.dart';
-import 'package:app_flowy/home/domain/page_context.dart';
-import 'package:app_flowy/home/presentation/widgets/prelude.dart';
+import 'package:app_flowy/workspace/application/home/home_bloc.dart';
+import 'package:app_flowy/workspace/application/home/home_watcher_bloc.dart';
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
+import 'package:app_flowy/workspace/presentation/widgets/prelude.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:flowy_infra/flowy_logger.dart';
 import 'package:flowy_infra_ui/style_widget/styled_container.dart';
@@ -11,7 +11,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:styled_widget/styled_widget.dart';
 
 import 'home_layout.dart';
-import 'widgets/fading_index_stack.dart';
 
 class HomeScreen extends StatelessWidget {
   static GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
@@ -29,10 +28,12 @@ class HomeScreen extends StatelessWidget {
       child: Scaffold(
         key: HomeScreen.scaffoldKey,
         body: BlocBuilder<HomeBloc, HomeState>(
+          buildWhen: (previous, current) => previous != current,
           builder: (context, state) {
             return StyledContainer(
               Theme.of(context).colorScheme.background,
-              child: _buildBody(state),
+              child: _buildBody(
+                  state, context.read<HomeBloc>().state.forceCollapse),
             );
           },
         ),
@@ -40,10 +41,10 @@ class HomeScreen extends StatelessWidget {
     );
   }
 
-  Widget _buildBody(HomeState state) {
+  Widget _buildBody(HomeState state, bool forceCollapse) {
     return LayoutBuilder(
       builder: (BuildContext context, BoxConstraints constraints) {
-        final layout = HomeLayout(context, constraints);
+        final layout = HomeLayout(context, constraints, forceCollapse);
         const homePage = HomePage();
         final menu = _buildHomeMenu(
           layout: layout,
@@ -68,16 +69,12 @@ class HomeScreen extends StatelessWidget {
     final homeBloc = context.read<HomeBloc>();
     Widget homeMenu = HomeMenu(
       pageContextChanged: (pageContext) {
-        pageContext.fold(
-          () => homeBloc.add(const HomeEvent.setPage(BlankPageContext())),
-          (pageContext) {
-            homeBloc.add(HomeEvent.setPage(pageContext));
-          },
-        );
+        getIt<HomePageStack>().setStackView(pageContext);
       },
       isCollapseChanged: (isCollapse) {
-        homeBloc.add(HomeEvent.showMenu(!isCollapse));
+        homeBloc.add(HomeEvent.forceCollapse(isCollapse));
       },
+      workspaceId: userDetail.workspace,
     );
     homeMenu = RepaintBoundary(child: homeMenu);
     homeMenu = FocusTraversalGroup(child: homeMenu);
@@ -139,25 +136,6 @@ class HomeScreen extends StatelessWidget {
   }
 }
 
-extension PageTypeExtension on PageType {
-  HomeStackPage builder(PageContext context) {
-    switch (this) {
-      case PageType.blank:
-        return BlankPage(context: context);
-    }
-  }
-}
-
-List<Widget> buildPagesWidget(PageContext pageContext) {
-  return PageType.values.map((pageType) {
-    if (pageType == pageContext.pageType) {
-      return pageType.builder(pageContext);
-    } else {
-      return const BlankPage(context: BlankPageContext());
-    }
-  }).toList();
-}
-
 class HomePage extends StatelessWidget {
   static GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
   // final Size size;
@@ -168,40 +146,44 @@ class HomePage extends StatelessWidget {
     Log.info('HomePage build');
     return Column(
       mainAxisAlignment: MainAxisAlignment.start,
-      children: const [
-        HomeTopBar(),
-        HomeIndexStack(),
-      ],
-    );
-  }
-}
-
-class HomeIndexStack extends StatelessWidget {
-  const HomeIndexStack({Key? key}) : super(key: key);
-  @override
-  Widget build(BuildContext context) {
-    return BlocBuilder<HomeBloc, HomeState>(
-      buildWhen: (p, c) {
-        if (p.pageContext != c.pageContext) {
-          Log.info(
-              'PageContext switch from ${p.pageContext.pageType} to ${c.pageContext.pageType}');
-        }
-        return p.pageContext != c.pageContext;
-      },
-      builder: (context, state) {
-        final pageContext = context.read<HomeBloc>().state.pageContext;
-        return Expanded(
+      children: [
+        getIt<HomePageStack>().stackTopBar(),
+        Expanded(
           child: Container(
             color: Colors.white,
             child: FocusTraversalGroup(
-              child: FadingIndexedStack(
-                index: pages.indexOf(pageContext.pageType),
-                children: buildPagesWidget(pageContext),
-              ),
+              child: getIt<HomePageStack>().stackWidget(),
             ),
           ),
-        );
-      },
+        ),
+      ],
     );
   }
 }
+
+// class HomeIndexStack extends StatelessWidget {
+//   const HomeIndexStack({Key? key}) : super(key: key);
+//   @override
+//   Widget build(BuildContext context) {
+//     return BlocBuilder<HomeBloc, HomeState>(
+//       buildWhen: (p, c) {
+//         if (p.pageContext != c.pageContext) {
+//           Log.info(
+//               'PageContext switch from ${p.pageContext.pageType} to ${c.pageContext.pageType}');
+//         }
+//         return p.pageContext != c.pageContext;
+//       },
+//       builder: (context, state) {
+//         final pageContext = context.read<HomeBloc>().state.pageContext;
+//         return Expanded(
+//           child: Container(
+//             color: Colors.white,
+//             child: FocusTraversalGroup(
+//               child: getIt<FlowyHomeIndexStack>().indexStack(pageContext),
+//             ),
+//           ),
+//         );
+//       },
+//     );
+//   }
+// }

+ 0 - 0
app_flowy/lib/home/presentation/home_sizes.dart → app_flowy/lib/workspace/presentation/home/home_sizes.dart


+ 39 - 0
app_flowy/lib/workspace/presentation/view/view_widget.dart

@@ -0,0 +1,39 @@
+import 'package:app_flowy/startup/startup.dart';
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:flutter/material.dart';
+
+class ViewWidget extends StatelessWidget {
+  final View view;
+  final Widget icon;
+  const ViewWidget({Key? key, required this.view, required this.icon})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return InkWell(onTap: _openView(context), child: buildContent());
+  }
+
+  Row buildContent() {
+    return Row(
+      children: [
+        icon,
+        const SizedBox(
+          width: 4,
+        ),
+        Text(
+          view.name,
+          textAlign: TextAlign.start,
+          style: const TextStyle(fontSize: 15),
+        )
+      ],
+    );
+  }
+
+  Function() _openView(BuildContext context) {
+    return () {
+      final stackView = stackViewFromView(view);
+      getIt<HomePageStack>().setStackView(stackView);
+    };
+  }
+}

+ 7 - 6
app_flowy/lib/home/presentation/widgets/blank_page.dart → app_flowy/lib/workspace/presentation/widgets/blank_page.dart

@@ -1,16 +1,17 @@
-import 'package:app_flowy/home/domain/page_context.dart';
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flutter/material.dart';
 
-class BlankPageContext extends PageContext {
-  const BlankPageContext() : super(PageType.blank, pageTitle: 'Blank');
+class BlankStackView extends HomeStackView {
+  const BlankStackView() : super(type: ViewType.Blank, title: 'Blank');
 
   @override
   List<Object> get props => [];
 }
 
-class BlankPage extends HomeStackPage {
-  const BlankPage({Key? key, required PageContext context})
-      : super(key: key, pageContext: context);
+class BlankPage extends HomeStackWidget {
+  const BlankPage({Key? key, required BlankStackView stackView})
+      : super(key: key, stackView: stackView);
 
   @override
   State<StatefulWidget> createState() => _BlankPageState();

+ 3 - 3
app_flowy/lib/home/presentation/widgets/edit_pannel/edit_pannel.dart → app_flowy/lib/workspace/presentation/widgets/edit_pannel/edit_pannel.dart

@@ -1,12 +1,12 @@
-import 'package:app_flowy/home/application/edit_pannel/edit_pannel_bloc.dart';
-import 'package:app_flowy/home/domain/edit_context.dart';
+import 'package:app_flowy/workspace/application/edit_pannel/edit_pannel_bloc.dart';
+import 'package:app_flowy/workspace/domain/edit_context.dart';
 import 'package:app_flowy/startup/startup.dart';
+import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_infra_ui/style_widget/styled_bar_title.dart';
 import 'package:flowy_infra_ui/style_widget/styled_close_button.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import '../../home_sizes.dart';
 
 class EditPannel extends StatelessWidget {
   late final EditPannelContext editContext;

+ 0 - 0
app_flowy/lib/home/presentation/widgets/edit_pannel/pannel_animation.dart → app_flowy/lib/workspace/presentation/widgets/edit_pannel/pannel_animation.dart


+ 0 - 0
app_flowy/lib/home/presentation/widgets/fading_index_stack.dart → app_flowy/lib/workspace/presentation/widgets/fading_index_stack.dart


+ 2 - 2
app_flowy/lib/home/presentation/widgets/home_stack_page.dart → app_flowy/lib/workspace/presentation/widgets/home_stack_page.dart

@@ -1,7 +1,7 @@
-import 'package:app_flowy/home/domain/page_context.dart';
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
 import 'package:flutter/material.dart';
 
 abstract class HomeStackPage extends StatefulWidget {
-  final PageContext pageContext;
+  final HomeStackView pageContext;
   const HomeStackPage({Key? key, required this.pageContext}) : super(key: key);
 }

+ 7 - 8
app_flowy/lib/home/presentation/widgets/home_top_bar.dart → app_flowy/lib/workspace/presentation/widgets/home_top_bar.dart

@@ -1,11 +1,9 @@
-import 'package:app_flowy/home/application/home_bloc.dart';
+import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-import '../home_sizes.dart';
 
 class HomeTopBar extends StatelessWidget {
-  const HomeTopBar({Key? key}) : super(key: key);
+  final String title;
+  const HomeTopBar({Key? key, required this.title}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -15,7 +13,7 @@ class HomeTopBar extends StatelessWidget {
       child: Column(
         crossAxisAlignment: CrossAxisAlignment.center,
         children: [
-          HomeTitle(),
+          HomeTitle(title: title),
         ],
       ),
     );
@@ -23,18 +21,19 @@ class HomeTopBar extends StatelessWidget {
 }
 
 class HomeTitle extends StatelessWidget {
+  final String title;
   final _editingController = TextEditingController(
     text: '',
   );
 
   HomeTitle({
     Key? key,
+    required this.title,
   }) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
-    _editingController.text =
-        context.read<HomeBloc>().state.pageContext.pageTitle;
+    _editingController.text = title;
 
     return Expanded(
       child: TextField(

+ 29 - 0
app_flowy/lib/workspace/presentation/widgets/menu/app_list.dart

@@ -0,0 +1,29 @@
+import 'package:app_flowy/workspace/presentation/app/app_widget.dart';
+import 'package:expandable/expandable.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:dartz/dartz.dart';
+
+class AppList extends StatelessWidget {
+  final Option<List<App>> apps;
+  const AppList({required this.apps, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return apps.fold(() {
+      return const Expanded(child: Text('You have no apps, create one?'));
+    }, (apps) {
+      return ExpandableTheme(
+          data: const ExpandableThemeData(
+            iconColor: Colors.blue,
+            useInkWell: true,
+          ),
+          child: Expanded(
+            child: ListView(
+              physics: const BouncingScrollPhysics(),
+              children: apps.map((app) => AppWidget(app)).toList(),
+            ),
+          ));
+    });
+  }
+}

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

@@ -0,0 +1,221 @@
+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/startup/tasks/application_task.dart';
+import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_infra/size.dart';
+import 'package:flowy_infra/text_style.dart';
+import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/style_widget/styled_text_input.dart';
+import 'package:flowy_infra_ui/widget/buttons/ok_cancel_button.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:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:styled_widget/styled_widget.dart';
+import 'package:textstyle_extensions/textstyle_extensions.dart';
+import 'app_list.dart';
+
+class HomeMenu extends StatelessWidget {
+  final Function(HomeStackView?) pageContextChanged;
+  final Function(bool) isCollapseChanged;
+  final String workspaceId;
+
+  const HomeMenu(
+      {Key? key,
+      required this.pageContextChanged,
+      required this.isCollapseChanged,
+      required this.workspaceId})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return MultiBlocProvider(
+      providers: [
+        BlocProvider<MenuBloc>(
+            create: (context) => getIt<MenuBloc>(param1: workspaceId)
+              ..add(const MenuEvent.initial())),
+        BlocProvider(
+            create: (context) => getIt<MenuWatchBloc>(param1: workspaceId)
+              ..add(const MenuWatchEvent.started())),
+      ],
+      child: MultiBlocListener(
+        listeners: [
+          BlocListener<MenuBloc, MenuState>(
+            listenWhen: (p, c) => p.stackView != c.stackView,
+            listener: (context, state) => pageContextChanged(state.stackView),
+          ),
+          BlocListener<MenuBloc, MenuState>(
+            listenWhen: (p, c) => p.isCollapse != c.isCollapse,
+            listener: (context, state) => isCollapseChanged(state.isCollapse),
+          )
+        ],
+        child: BlocBuilder<MenuBloc, MenuState>(
+          builder: (context, state) => _renderBody(context),
+        ),
+      ),
+    );
+  }
+
+  Widget _renderBody(BuildContext context) {
+    return Container(
+      color: Theme.of(context).colorScheme.primaryVariant,
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        children: [
+          const MenuTopBar(),
+          _renderAppList(context),
+          _renderNewButton(context),
+        ],
+      ).padding(horizontal: Insets.sm),
+    );
+  }
+
+  Widget _renderAppList(BuildContext context) {
+    return BlocBuilder<MenuWatchBloc, MenuWatchState>(
+      builder: (context, state) => state.map(
+        initial: (_) => BlocBuilder<MenuBloc, MenuState>(
+          builder: (context, state) {
+            return AppList(apps: state.apps);
+          },
+        ),
+        loadApps: (s) => AppList(apps: some(s.apps)),
+        loadFail: (s) => FlowyErrorPage(s.error.toString()),
+      ),
+    );
+  }
+
+  Widget _renderNewButton(BuildContext context) {
+    return NewAppButton(
+      createAppCallback: (appName) =>
+          context.read<MenuBloc>().add(MenuEvent.createApp(appName, desc: "")),
+    );
+  }
+}
+
+class MenuTopBar extends StatelessWidget {
+  const MenuTopBar({Key? key}) : super(key: key);
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<MenuBloc, MenuState>(
+      builder: (context, state) {
+        return SizedBox(
+          height: HomeSizes.menuTopBarHeight,
+          child: Row(
+            children: [
+              const Text(
+                'AppFlowy',
+                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
+              ).constrained(minWidth: 100),
+              const Spacer(),
+              IconButton(
+                icon: const Icon(Icons.arrow_left),
+                onPressed: () =>
+                    context.read<MenuBloc>().add(const MenuEvent.collapse()),
+              ),
+            ],
+          ),
+        );
+      },
+    );
+  }
+}
+
+class NewAppButton extends StatelessWidget {
+  final Function(String)? createAppCallback;
+
+  const NewAppButton({this.createAppCallback, Key? key}) : super(key: key);
+  @override
+  Widget build(BuildContext context) {
+    return SizedBox(
+      height: HomeSizes.menuAddButtonHeight,
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.start,
+        children: [
+          const Icon(Icons.add),
+          const SizedBox(
+            width: 10,
+          ),
+          TextButton(
+            onPressed: () async => await _showCreateAppDialog(context),
+            child: _buttonTitle(),
+          )
+        ],
+      ),
+    );
+  }
+
+  Widget _buttonTitle() {
+    return const Text('New App',
+        style: TextStyle(
+          color: Colors.black,
+          fontWeight: FontWeight.bold,
+          fontSize: 20,
+        ));
+  }
+
+  Future<void> _showCreateAppDialog(BuildContext context) async {
+    await Dialogs.showWithContext(CreateAppDialogContext(
+      confirm: (appName) {
+        if (appName.isNotEmpty && createAppCallback != null) {
+          createAppCallback!(appName);
+        }
+      },
+    ), context);
+  }
+}
+
+//ignore: must_be_immutable
+class CreateAppDialogContext extends DialogContext {
+  String appName;
+  final Function(String)? confirm;
+
+  CreateAppDialogContext({this.appName = "", this.confirm})
+      : super(identifier: 'CreateAppDialogContext');
+
+  @override
+  Widget buildWiget(BuildContext context) {
+    final theme = context.watch<AppTheme>();
+    return StyledDialog(
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: <Widget>[
+          ...[
+            Text('Create App'.toUpperCase(),
+                style: TextStyles.T1.textColor(theme.accent1Darker)),
+            VSpace(Insets.sm * 1.5),
+            // Container(color: theme.greyWeak.withOpacity(.35), height: 1),
+            VSpace(Insets.m * 1.5),
+          ],
+          StyledFormTextInput(
+            hintText: "App name",
+            onChanged: (text) {
+              appName = text;
+            },
+          ),
+          SizedBox(height: Insets.l),
+          OkCancelButton(
+            onOkPressed: () {
+              if (confirm != null) {
+                confirm!(appName);
+                AppGlobals.nav.pop();
+              }
+            },
+            onCancelPressed: () {
+              AppGlobals.nav.pop();
+            },
+          )
+        ],
+      ),
+    );
+  }
+
+  @override
+  List<Object> get props => [identifier];
+
+  @override
+  bool get barrierDismissable => false;
+}

+ 0 - 0
app_flowy/lib/home/presentation/widgets/menu/hom_menu_size.dart → app_flowy/lib/workspace/presentation/widgets/menu/menu_size.dart


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

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

+ 1 - 1
app_flowy/lib/home/presentation/widgets/prelude.dart → app_flowy/lib/workspace/presentation/widgets/prelude.dart

@@ -2,4 +2,4 @@ export './blank_page.dart';
 export './edit_pannel/edit_pannel.dart';
 export './edit_pannel/pannel_animation.dart';
 export './home_top_bar.dart';
-export './menu/home_menu.dart';
+export 'menu/menu.dart';

+ 1 - 1
app_flowy/macos/Podfile.lock

@@ -49,4 +49,4 @@ SPEC CHECKSUMS:
 
 PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
 
-COCOAPODS: 1.10.1
+COCOAPODS: 1.9.3

+ 13 - 19
app_flowy/packages/flowy_editor/lib/src/service/controller.dart

@@ -38,8 +38,7 @@ class EditorController extends ChangeNotifier {
       );
 
   Style getSelectionStyle() =>
-      document.collectStyle(selection.start, selection.end - selection.start)
-        ..mergeAll(toggledStyle);
+      document.collectStyle(selection.start, selection.end - selection.start)..mergeAll(toggledStyle);
 
   bool get hasUndo => document.hasUndo;
 
@@ -59,6 +58,12 @@ class EditorController extends ChangeNotifier {
     }
   }
 
+  Future<bool> save() async {
+    document.toDelta().toJson();
+    // TODO: vedon - Save document to database
+    return true;
+  }
+
   @override
   void dispose() {
     document.close();
@@ -75,9 +80,7 @@ class EditorController extends ChangeNotifier {
   }
 
   void formatText(int index, int length, Attribute? attribute) {
-    if (length == 0 &&
-        attribute!.isInline &&
-        attribute.key != Attribute.link.key) {
+    if (length == 0 && attribute!.isInline && attribute.key != Attribute.link.key) {
       toggledStyle = toggledStyle.put(attribute);
     }
 
@@ -92,24 +95,16 @@ class EditorController extends ChangeNotifier {
     notifyListeners();
   }
 
-  void replaceText(
-      int index, int length, Object? data, TextSelection? textSelection) {
+  void replaceText(int index, int length, Object? data, TextSelection? textSelection) {
     assert(data is String || data is Embeddable);
 
     Delta? delta;
     if (length > 0 || data is! String || data.isNotEmpty) {
       delta = document.replace(index, length, data);
-      var shouldRetainDelta = toggledStyle.isNotEmpty &&
-          delta.isNotEmpty &&
-          delta.length <= 2 &&
-          delta.last.isInsert;
-      if (shouldRetainDelta &&
-          toggledStyle.isNotEmpty &&
-          delta.length == 2 &&
-          delta.last.data == '\n') {
+      var shouldRetainDelta = toggledStyle.isNotEmpty && delta.isNotEmpty && delta.length <= 2 && delta.last.isInsert;
+      if (shouldRetainDelta && toggledStyle.isNotEmpty && delta.length == 2 && delta.last.data == '\n') {
         // if all attributes are inline, shouldRetainDelta should be false
-        final anyAttributeNotInline =
-            toggledStyle.values.any((attr) => !attr.isInline);
+        final anyAttributeNotInline = toggledStyle.values.any((attr) => !attr.isInline);
         shouldRetainDelta &= anyAttributeNotInline;
       }
       if (shouldRetainDelta) {
@@ -151,8 +146,7 @@ class EditorController extends ChangeNotifier {
 
     textSelection = selection.copyWith(
       baseOffset: delta.transformPosition(selection.baseOffset, force: false),
-      extentOffset:
-          delta.transformPosition(selection.extentOffset, force: false),
+      extentOffset: delta.transformPosition(selection.extentOffset, force: false),
     );
     if (selection != textSelection) {
       _updateSelection(textSelection, source);

+ 4 - 2
app_flowy/packages/flowy_editor/lib/src/service/keyboard.dart

@@ -8,6 +8,7 @@ enum InputShortcut {
   COPY,
   PASTE,
   SELECT_ALL,
+  SAVE,
 }
 
 typedef CursorMoveCallback = void Function(
@@ -46,6 +47,7 @@ class KeyboardListener {
     LogicalKeyboardKey.keyC,
     LogicalKeyboardKey.keyV,
     LogicalKeyboardKey.keyX,
+    LogicalKeyboardKey.keyS,
     LogicalKeyboardKey.delete,
     LogicalKeyboardKey.backspace,
   };
@@ -78,6 +80,7 @@ class KeyboardListener {
     LogicalKeyboardKey.keyC: InputShortcut.COPY,
     LogicalKeyboardKey.keyV: InputShortcut.PASTE,
     LogicalKeyboardKey.keyA: InputShortcut.SELECT_ALL,
+    LogicalKeyboardKey.keyS: InputShortcut.SAVE,
   };
 
   bool handleRawKeyEvent(RawKeyEvent event) {
@@ -89,8 +92,7 @@ class KeyboardListener {
       return false;
     }
 
-    final keysPressed =
-        LogicalKeyboardKey.collapseSynonyms(RawKeyboard.instance.keysPressed);
+    final keysPressed = LogicalKeyboardKey.collapseSynonyms(RawKeyboard.instance.keysPressed);
     final key = event.logicalKey;
     final isMacOS = event.data is RawKeyEventDataMacOs;
     final modifierKeys = isMacOS ? _osxModifierKeys : _winModifierKeys;

+ 68 - 135
app_flowy/packages/flowy_editor/lib/src/widget/raw_editor.dart

@@ -1,6 +1,7 @@
 import 'dart:async';
 import 'dart:convert';
 import 'dart:math' as math;
+import 'dart:developer';
 
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/foundation.dart';
@@ -59,8 +60,7 @@ class RawEditor extends StatefulWidget {
     this.embedBuilder,
   )   : assert(maxHeight == null || maxHeight > 0, 'maxHeight cannot be null'),
         assert(minHeight == null || minHeight >= 0, 'minHeight cannot be null'),
-        assert(
-            maxHeight == null || minHeight == null || maxHeight >= minHeight),
+        assert(maxHeight == null || minHeight == null || maxHeight >= minHeight),
         showCursor = showCursor ?? true,
         super(key: key);
 
@@ -111,10 +111,7 @@ abstract class EditorState extends State<RawEditor> {
 }
 
 class _RawEditorState extends EditorState
-    with
-        AutomaticKeepAliveClientMixin<RawEditor>,
-        WidgetsBindingObserver,
-        TickerProviderStateMixin<RawEditor>
+    with AutomaticKeepAliveClientMixin<RawEditor>, WidgetsBindingObserver, TickerProviderStateMixin<RawEditor>
     implements TextSelectionDelegate, TextInputClient {
   final GlobalKey _editorKey = GlobalKey();
   final List<TextEditingValue> _sentRemoteValues = [];
@@ -132,8 +129,7 @@ class _RawEditorState extends EditorState
   bool _didAutoFocus = false;
   bool _keyboardVisible = false;
   DefaultStyles? _styles;
-  final ClipboardStatusNotifier? _clipboardStatus =
-      kIsWeb ? null : ClipboardStatusNotifier();
+  final ClipboardStatusNotifier? _clipboardStatus = kIsWeb ? null : ClipboardStatusNotifier();
   final LayerLink _toolbarLayerLink = LayerLink();
   final LayerLink _startHandleLayerLink = LayerLink();
   final LayerLink _endHandleLayerLink = LayerLink();
@@ -181,78 +177,57 @@ class _RawEditorState extends EditorState
         downKey = key == LogicalKeyboardKey.arrowDown;
 
     if ((rightKey || leftKey) && !(rightKey && leftKey)) {
-      newSelection = _jumpToBeginOrEndOfWord(newSelection, wordModifier,
-          leftKey, rightKey, plainText, lineModifier, shift);
+      newSelection =
+          _jumpToBeginOrEndOfWord(newSelection, wordModifier, leftKey, rightKey, plainText, lineModifier, shift);
     }
 
     if (downKey || upKey) {
-      newSelection = _handleMovingCursorVertically(
-          upKey, downKey, shift, selection, newSelection, plainText);
+      newSelection = _handleMovingCursorVertically(upKey, downKey, shift, selection, newSelection, plainText);
     }
 
     if (!shift) {
-      newSelection =
-          _placeCollapsedSelection(selection, newSelection, leftKey, rightKey);
+      newSelection = _placeCollapsedSelection(selection, newSelection, leftKey, rightKey);
     }
 
     widget.controller.updateSelection(newSelection, ChangeSource.LOCAL);
   }
 
-  TextSelection _placeCollapsedSelection(TextSelection selection,
-      TextSelection newSelection, bool leftKey, bool rightKey) {
+  TextSelection _placeCollapsedSelection(
+      TextSelection selection, TextSelection newSelection, bool leftKey, bool rightKey) {
     var newOffset = newSelection.extentOffset;
     if (!selection.isCollapsed) {
       if (leftKey) {
-        newOffset = newSelection.baseOffset < newSelection.extentOffset
-            ? newSelection.baseOffset
-            : newSelection.extentOffset;
+        newOffset =
+            newSelection.baseOffset < newSelection.extentOffset ? newSelection.baseOffset : newSelection.extentOffset;
       } else if (rightKey) {
-        newOffset = newSelection.baseOffset > newSelection.extentOffset
-            ? newSelection.baseOffset
-            : newSelection.extentOffset;
+        newOffset =
+            newSelection.baseOffset > newSelection.extentOffset ? newSelection.baseOffset : newSelection.extentOffset;
       }
     }
     return TextSelection.fromPosition(TextPosition(offset: newOffset));
   }
 
   TextSelection _handleMovingCursorVertically(
-      bool upKey,
-      bool downKey,
-      bool shift,
-      TextSelection selection,
-      TextSelection newSelection,
-      String plainText) {
-    final originPosition = TextPosition(
-        offset: upKey ? selection.baseOffset : selection.extentOffset);
+      bool upKey, bool downKey, bool shift, TextSelection selection, TextSelection newSelection, String plainText) {
+    final originPosition = TextPosition(offset: upKey ? selection.baseOffset : selection.extentOffset);
 
     final child = getRenderEditor()!.childAtPosition(originPosition);
-    final localPosition = TextPosition(
-        offset: originPosition.offset - child.container.documentOffset);
+    final localPosition = TextPosition(offset: originPosition.offset - child.container.documentOffset);
 
-    var position = upKey
-        ? child.getPositionAbove(localPosition)
-        : child.getPositionBelow(localPosition);
+    var position = upKey ? child.getPositionAbove(localPosition) : child.getPositionBelow(localPosition);
 
     if (position == null) {
-      final sibling = upKey
-          ? getRenderEditor()!.childBefore(child)
-          : getRenderEditor()!.childAfter(child);
+      final sibling = upKey ? getRenderEditor()!.childBefore(child) : getRenderEditor()!.childAfter(child);
       if (sibling == null) {
         position = TextPosition(offset: upKey ? 0 : plainText.length - 1);
       } else {
-        final finalOffset = Offset(
-            child.getOffsetForCaret(localPosition).dx,
-            sibling
-                .getOffsetForCaret(TextPosition(
-                    offset: upKey ? sibling.container.length - 1 : 0))
-                .dy);
+        final finalOffset = Offset(child.getOffsetForCaret(localPosition).dx,
+            sibling.getOffsetForCaret(TextPosition(offset: upKey ? sibling.container.length - 1 : 0)).dy);
         final siblingPosition = sibling.getPositionForOffset(finalOffset);
-        position = TextPosition(
-            offset: sibling.container.documentOffset + siblingPosition.offset);
+        position = TextPosition(offset: sibling.container.documentOffset + siblingPosition.offset);
       }
     } else {
-      position = TextPosition(
-          offset: child.container.documentOffset + position.offset);
+      position = TextPosition(offset: child.container.documentOffset + position.offset);
     }
 
     if (position.offset == newSelection.extentOffset) {
@@ -275,47 +250,33 @@ class _RawEditorState extends EditorState
     return newSelection;
   }
 
-  TextSelection _jumpToBeginOrEndOfWord(
-      TextSelection newSelection,
-      bool wordModifier,
-      bool leftKey,
-      bool rightKey,
-      String plainText,
-      bool lineModifier,
-      bool shift) {
+  TextSelection _jumpToBeginOrEndOfWord(TextSelection newSelection, bool wordModifier, bool leftKey, bool rightKey,
+      String plainText, bool lineModifier, bool shift) {
     if (wordModifier) {
       if (leftKey) {
         final textSelection = getRenderEditor()!.selectWordAtPosition(
-            TextPosition(
-                offset: _previousCharacter(
-                    newSelection.extentOffset, plainText, false)));
+            TextPosition(offset: _previousCharacter(newSelection.extentOffset, plainText, false)));
         return newSelection.copyWith(extentOffset: textSelection.baseOffset);
       }
-      final textSelection = getRenderEditor()!.selectWordAtPosition(
-          TextPosition(
-              offset:
-                  _nextCharacter(newSelection.extentOffset, plainText, false)));
+      final textSelection = getRenderEditor()!
+          .selectWordAtPosition(TextPosition(offset: _nextCharacter(newSelection.extentOffset, plainText, false)));
       return newSelection.copyWith(extentOffset: textSelection.extentOffset);
     } else if (lineModifier) {
       if (leftKey) {
         final textSelection = getRenderEditor()!.selectLineAtPosition(
-            TextPosition(
-                offset: _previousCharacter(
-                    newSelection.extentOffset, plainText, false)));
+            TextPosition(offset: _previousCharacter(newSelection.extentOffset, plainText, false)));
         return newSelection.copyWith(extentOffset: textSelection.baseOffset);
       }
       final startPoint = newSelection.extentOffset;
       if (startPoint < plainText.length) {
-        final textSelection = getRenderEditor()!
-            .selectLineAtPosition(TextPosition(offset: startPoint));
+        final textSelection = getRenderEditor()!.selectLineAtPosition(TextPosition(offset: startPoint));
         return newSelection.copyWith(extentOffset: textSelection.extentOffset);
       }
       return newSelection;
     }
 
     if (rightKey && newSelection.extentOffset < plainText.length) {
-      final nextExtent =
-          _nextCharacter(newSelection.extentOffset, plainText, true);
+      final nextExtent = _nextCharacter(newSelection.extentOffset, plainText, true);
       final distance = nextExtent - newSelection.extentOffset;
       newSelection = newSelection.copyWith(extentOffset: nextExtent);
       if (shift) {
@@ -325,8 +286,7 @@ class _RawEditorState extends EditorState
     }
 
     if (leftKey && newSelection.extentOffset > 0) {
-      final previousExtent =
-          _previousCharacter(newSelection.extentOffset, plainText, true);
+      final previousExtent = _previousCharacter(newSelection.extentOffset, plainText, true);
       final distance = newSelection.extentOffset - previousExtent;
       newSelection = newSelection.copyWith(extentOffset: previousExtent);
       if (shift) {
@@ -366,9 +326,7 @@ class _RawEditorState extends EditorState
     var count = 0;
     int? lastNonWhitespace;
     for (final currentString in string.characters) {
-      if (!includeWhitespace &&
-          !WHITE_SPACE.contains(
-              currentString.characters.first.toString().codeUnitAt(0))) {
+      if (!includeWhitespace && !WHITE_SPACE.contains(currentString.characters.first.toString().codeUnitAt(0))) {
         lastNonWhitespace = count;
       }
       if (count + currentString.length >= index) {
@@ -379,8 +337,7 @@ class _RawEditorState extends EditorState
     return 0;
   }
 
-  bool get hasConnection =>
-      _textInputConnection != null && _textInputConnection!.attached;
+  bool get hasConnection => _textInputConnection != null && _textInputConnection!.attached;
 
   void openConnectionIfNeeded() {
     if (!shouldCreateInputConnection) {
@@ -431,8 +388,7 @@ class _RawEditorState extends EditorState
       return;
     }
 
-    final shouldRemember =
-        textEditingValue.text != _lastKnownRemoteTextEditingValue!.text;
+    final shouldRemember = textEditingValue.text != _lastKnownRemoteTextEditingValue!.text;
     _lastKnownRemoteTextEditingValue = actualValue;
     _textInputConnection!.setEditingState(actualValue);
     if (shouldRemember) {
@@ -441,8 +397,7 @@ class _RawEditorState extends EditorState
   }
 
   @override
-  TextEditingValue? get currentTextEditingValue =>
-      _lastKnownRemoteTextEditingValue;
+  TextEditingValue? get currentTextEditingValue => _lastKnownRemoteTextEditingValue;
 
   @override
   AutofillScope? get currentAutofillScope => null;
@@ -474,8 +429,7 @@ class _RawEditorState extends EditorState
     final text = value.text;
     final cursorPosition = value.selection.extentOffset;
     final diff = getDiff(oldText, text, cursorPosition);
-    widget.controller.replaceText(
-        diff.start, diff.deleted.length, diff.inserted, value.selection);
+    widget.controller.replaceText(diff.start, diff.deleted.length, diff.inserted, value.selection);
   }
 
   @override
@@ -525,11 +479,8 @@ class _RawEditorState extends EditorState
     super.build(context);
 
     var _doc = widget.controller.document;
-    if (_doc.isEmpty() &&
-        !widget.focusNode.hasFocus &&
-        widget.placeholder != null) {
-      _doc = Document.fromJson(jsonDecode(
-          '[{"attributes":{"placeholder":true},"insert":"${widget.placeholder}\\n"}]'));
+    if (_doc.isEmpty() && !widget.focusNode.hasFocus && widget.placeholder != null) {
+      _doc = Document.fromJson(jsonDecode('[{"attributes":{"placeholder":true},"insert":"${widget.placeholder}\\n"}]'));
     }
 
     Widget child = CompositedTransformTarget(
@@ -552,8 +503,7 @@ class _RawEditorState extends EditorState
     );
 
     if (widget.scrollable) {
-      final baselinePadding =
-          EdgeInsets.only(top: _styles!.paragraph!.verticalSpacing.item1);
+      final baselinePadding = EdgeInsets.only(top: _styles!.paragraph!.verticalSpacing.item1);
       child = BaselineProxy(
         textStyle: _styles!.paragraph!.style,
         padding: baselinePadding,
@@ -584,8 +534,7 @@ class _RawEditorState extends EditorState
     );
   }
 
-  void _handleSelectionChanged(
-      TextSelection selection, SelectionChangedCause cause) {
+  void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) {
     widget.controller.updateSelection(selection, ChangeSource.LOCAL);
 
     _selectionOverlay?.handlesVisible = _shouldShowSelectionHandles();
@@ -614,9 +563,7 @@ class _RawEditorState extends EditorState
             _styles,
             widget.enableInteractiveSelection,
             _hasFocus,
-            attrs.containsKey(Attribute.codeBlock.key)
-                ? const EdgeInsets.all(16)
-                : null,
+            attrs.containsKey(Attribute.codeBlock.key) ? const EdgeInsets.all(16) : null,
             widget.embedBuilder,
             _cursorController,
             indentLevelCounts);
@@ -628,8 +575,7 @@ class _RawEditorState extends EditorState
     return result;
   }
 
-  EditableTextLine _getEditableTextLineFromNode(
-      Line node, BuildContext context) {
+  EditableTextLine _getEditableTextLineFromNode(Line node, BuildContext context) {
     final textLine = TextLine(
       line: node,
       textDirection: _textDirection,
@@ -652,8 +598,7 @@ class _RawEditorState extends EditorState
     return editableTextLine;
   }
 
-  Tuple2<double, double> _getVerticalSpacingForLine(
-      Line line, DefaultStyles? defaultStyles) {
+  Tuple2<double, double> _getVerticalSpacingForLine(Line line, DefaultStyles? defaultStyles) {
     final attrs = line.style.attributes;
     if (attrs.containsKey(Attribute.header.key)) {
       final int? level = attrs[Attribute.header.key]!.value;
@@ -678,8 +623,7 @@ class _RawEditorState extends EditorState
     return defaultStyles!.paragraph!.verticalSpacing;
   }
 
-  Tuple2<double, double> _getVerticalSpacingForBlock(
-      Block node, DefaultStyles? defaultStyles) {
+  Tuple2<double, double> _getVerticalSpacingForBlock(Block node, DefaultStyles? defaultStyles) {
     final attrs = node.style.attributes;
     if (attrs.containsKey(Attribute.quoteBlock.key)) {
       return defaultStyles!.quote!.verticalSpacing;
@@ -722,8 +666,7 @@ class _RawEditorState extends EditorState
     } else {
       _keyboardVisibilityController = KeyboardVisibilityController();
       _keyboardVisible = _keyboardVisibilityController!.isVisible;
-      _keyboardVisibilitySubscription =
-          _keyboardVisibilityController?.onChange.listen((visible) {
+      _keyboardVisibilitySubscription = _keyboardVisibilityController?.onChange.listen((visible) {
         _keyboardVisible = visible;
         if (visible) {
           _onChangeTextEditingValue();
@@ -746,9 +689,7 @@ class _RawEditorState extends EditorState
     super.didChangeDependencies();
     final parentStyles = EditorStyles.getStyles(context, true);
     final defaultStyles = DefaultStyles.getInstance(context);
-    _styles = (parentStyles != null)
-        ? defaultStyles.merge(parentStyles)
-        : defaultStyles;
+    _styles = (parentStyles != null) ? defaultStyles.merge(parentStyles) : defaultStyles;
 
     if (widget.customStyles != null) {
       _styles = _styles!.merge(widget.customStyles!);
@@ -808,8 +749,7 @@ class _RawEditorState extends EditorState
   }
 
   bool _shouldShowSelectionHandles() {
-    return widget.showSelectionHandles &&
-        !widget.controller.selection.isCollapsed;
+    return widget.showSelectionHandles && !widget.controller.selection.isCollapsed;
   }
 
   void handleDelete(bool forward) {
@@ -820,8 +760,7 @@ class _RawEditorState extends EditorState
     var textAfter = selection.textAfter(plainText);
     if (selection.isCollapsed) {
       if (!forward && textBefore.isNotEmpty) {
-        final characterBoundary =
-            _previousCharacter(textBefore.length, textBefore, true);
+        final characterBoundary = _previousCharacter(textBefore.length, textBefore, true);
         textBefore = textBefore.substring(0, characterBoundary);
         cursorPosition = characterBoundary;
       }
@@ -844,10 +783,16 @@ class _RawEditorState extends EditorState
   Future<void> handleShortcut(InputShortcut? shortcut) async {
     final selection = widget.controller.selection;
     final plainText = textEditingValue.text;
+    if (shortcut == InputShortcut.SAVE) {
+      bool saved = await widget.controller.save();
+      if (!saved) {
+        log('Unabled to save document.');
+      }
+      return;
+    }
     if (shortcut == InputShortcut.COPY) {
       if (!selection.isCollapsed) {
-        await Clipboard.setData(
-            ClipboardData(text: selection.textInside(plainText)));
+        await Clipboard.setData(ClipboardData(text: selection.textInside(plainText)));
       }
       return;
     }
@@ -864,8 +809,7 @@ class _RawEditorState extends EditorState
         );
 
         textEditingValue = TextEditingValue(
-          text:
-              selection.textBefore(plainText) + selection.textAfter(plainText),
+          text: selection.textBefore(plainText) + selection.textAfter(plainText),
           selection: TextSelection.collapsed(offset: selection.start),
         );
       }
@@ -883,8 +827,7 @@ class _RawEditorState extends EditorState
       }
       return;
     }
-    if (shortcut == InputShortcut.SELECT_ALL &&
-        widget.enableInteractiveSelection) {
+    if (shortcut == InputShortcut.SELECT_ALL && widget.enableInteractiveSelection) {
       widget.controller.updateSelection(
           selection.copyWith(
             baseOffset: 0,
@@ -938,16 +881,14 @@ class _RawEditorState extends EditorState
   void _onChangeTextEditingValue() {
     _showCaretOnScreen();
     updateRemoteValueIfNeeded();
-    _cursorController.startOrStopCursorTimerIfNeeded(
-        _hasFocus, widget.controller.selection);
+    _cursorController.startOrStopCursorTimerIfNeeded(_hasFocus, widget.controller.selection);
     if (hasConnection) {
       _cursorController
         ..stopCursorTimer(resetCharTicks: false)
         ..startCursorTimer();
     }
 
-    SchedulerBinding.instance!.addPostFrameCallback(
-        (_) => _updateOrDisposeSelectionOverlayIfNeeded());
+    SchedulerBinding.instance!.addPostFrameCallback((_) => _updateOrDisposeSelectionOverlayIfNeeded());
     if (mounted) {
       setState(() {
         // Use widget.controller.value in build()
@@ -990,8 +931,7 @@ class _RawEditorState extends EditorState
 
   void _handleFocusChanged() {
     openOrCloseConnection();
-    _cursorController.startOrStopCursorTimerIfNeeded(
-        _hasFocus, widget.controller.selection);
+    _cursorController.startOrStopCursorTimerIfNeeded(_hasFocus, widget.controller.selection);
     _updateOrDisposeSelectionOverlayIfNeeded();
     if (_hasFocus) {
       WidgetsBinding.instance!.addObserver(this);
@@ -1022,8 +962,7 @@ class _RawEditorState extends EditorState
       _showCaretOnScreenScheduled = false;
 
       final viewport = RenderAbstractViewport.of(getRenderEditor())!;
-      final editorOffset = getRenderEditor()!
-          .localToGlobal(const Offset(0, 0), ancestor: viewport);
+      final editorOffset = getRenderEditor()!.localToGlobal(const Offset(0, 0), ancestor: viewport);
       final offsetInViewport = _scrollController!.offset + editorOffset.dy;
 
       final offset = getRenderEditor()!.getOffsetToRevealCursor(
@@ -1106,8 +1045,7 @@ class _RawEditorState extends EditorState
       final value = textEditingValue;
       final data = await Clipboard.getData(Clipboard.kTextPlain);
       if (data != null) {
-        final length =
-            textEditingValue.selection.end - textEditingValue.selection.start;
+        final length = textEditingValue.selection.end - textEditingValue.selection.start;
         widget.controller.replaceText(
           value.selection.start,
           length,
@@ -1116,9 +1054,7 @@ class _RawEditorState extends EditorState
         );
         // move cursor to the end of pasted text selection
         widget.controller.updateSelection(
-            TextSelection.collapsed(
-                offset: value.selection.start + data.text!.length),
-            ChangeSource.LOCAL);
+            TextSelection.collapsed(offset: value.selection.start + data.text!.length), ChangeSource.LOCAL);
       }
     }
   }
@@ -1128,8 +1064,7 @@ class _RawEditorState extends EditorState
     if (data == null) {
       return false;
     }
-    return textEditingValue.text.length - value.text.length ==
-        data.text!.length;
+    return textEditingValue.text.length - value.text.length == data.text!.length;
   }
 
   @override
@@ -1162,8 +1097,7 @@ class _RawEditorState extends EditorState
   }
 
   @override
-  void userUpdateTextEditingValue(
-      TextEditingValue value, SelectionChangedCause cause) {
+  void userUpdateTextEditingValue(TextEditingValue value, SelectionChangedCause cause) {
     // TODO: implement userUpdateTextEditingValue
   }
 }
@@ -1213,8 +1147,7 @@ class _Editor extends MultiChildRenderObjectWidget {
   }
 
   @override
-  void updateRenderObject(
-      BuildContext context, covariant RenderEditor renderObject) {
+  void updateRenderObject(BuildContext context, covariant RenderEditor renderObject) {
     renderObject
       ..document = document
       ..container = document.root

+ 4 - 4
app_flowy/packages/flowy_infra/pubspec.lock

@@ -7,7 +7,7 @@ packages:
       name: async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.7.0"
+    version: "2.6.1"
   boolean_selector:
     dependency: transitive
     description:
@@ -28,7 +28,7 @@ packages:
       name: charcode
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.3.1"
+    version: "1.2.0"
   clock:
     dependency: transitive
     description:
@@ -101,7 +101,7 @@ packages:
       name: meta
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.4.0"
+    version: "1.3.0"
   path:
     dependency: transitive
     description:
@@ -155,7 +155,7 @@ packages:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.4.1"
+    version: "0.3.0"
   textstyle_extensions:
     dependency: "direct main"
     description:

Деякі файли не було показано, через те що забагато файлів було змінено