Преглед изворни кода

combine event and state into bloc & add watch bloc of workspace

appflowy пре 4 година
родитељ
комит
9b9d462f36
36 измењених фајлова са 1823 додато и 391 уклоњено
  1. 22 3
      app_flowy/lib/home/application/app/app_bloc.dart
  2. 23 23
      app_flowy/lib/home/application/app/app_bloc.freezed.dart
  3. 0 8
      app_flowy/lib/home/application/app/app_event.dart
  4. 0 16
      app_flowy/lib/home/application/app/app_state.dart
  5. 56 0
      app_flowy/lib/home/application/app/app_watch_bloc.dart
  6. 683 0
      app_flowy/lib/home/application/app/app_watch_bloc.freezed.dart
  7. 31 3
      app_flowy/lib/home/application/home_bloc.dart
  8. 0 15
      app_flowy/lib/home/application/home_event.dart
  9. 0 18
      app_flowy/lib/home/application/home_state.dart
  10. 33 17
      app_flowy/lib/home/application/menu/menu_bloc.dart
  11. 0 168
      app_flowy/lib/home/application/menu/menu_bloc.freezed.dart
  12. 0 11
      app_flowy/lib/home/application/menu/menu_event.dart
  13. 0 18
      app_flowy/lib/home/application/menu/menu_state.dart
  14. 64 0
      app_flowy/lib/home/application/menu/menu_watch.dart
  15. 682 0
      app_flowy/lib/home/application/menu/menu_watch.freezed.dart
  16. 2 0
      app_flowy/lib/home/domain/i_app.dart
  17. 3 2
      app_flowy/lib/home/domain/i_workspace.dart
  18. 20 6
      app_flowy/lib/home/infrastructure/deps_resolver.dart
  19. 7 0
      app_flowy/lib/home/infrastructure/i_app_impl.dart
  20. 10 6
      app_flowy/lib/home/infrastructure/i_workspace_impl.dart
  21. 15 5
      app_flowy/lib/home/infrastructure/repos/app_repo.dart
  22. 19 8
      app_flowy/lib/home/infrastructure/repos/workspace_repo.dart
  23. 49 30
      app_flowy/lib/home/presentation/widgets/app/app_list_widget.dart
  24. 21 2
      app_flowy/lib/home/presentation/widgets/app/app_widget.dart
  25. 29 0
      app_flowy/lib/home/presentation/widgets/menu/app_list.dart
  26. 33 11
      app_flowy/lib/home/presentation/widgets/menu/menu.dart
  27. 0 0
      app_flowy/lib/home/presentation/widgets/menu/menu_size.dart
  28. 2 2
      app_flowy/lib/home/presentation/widgets/menu/prelude.dart
  29. 1 1
      app_flowy/lib/home/presentation/widgets/prelude.dart
  30. 3 3
      app_flowy/lib/startup/tasks/application_task.dart
  31. 1 1
      app_flowy/lib/startup/tasks/prelude.dart
  32. 4 4
      app_flowy/packages/flowy_infra_ui/example/pubspec.lock
  33. 4 4
      app_flowy/packages/flowy_infra_ui/pubspec.lock
  34. 1 1
      app_flowy/pubspec.lock
  35. 1 1
      app_flowy/pubspec.yaml
  36. 4 4
      rust-lib/dart-ffi/Cargo.toml

+ 22 - 3
app_flowy/lib/home/application/app/app_bloc.dart

@@ -1,13 +1,10 @@
 import 'package:app_flowy/home/domain/i_app.dart';
 import 'package:app_flowy/home/domain/i_app.dart';
-import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:dartz/dartz.dart';
 import 'package:dartz/dartz.dart';
 
 
-part 'app_event.dart';
-part 'app_state.dart';
 part 'app_bloc.freezed.dart';
 part 'app_bloc.freezed.dart';
 
 
 class AppBloc extends Bloc<AppEvent, AppState> {
 class AppBloc extends Bloc<AppEvent, AppState> {
@@ -31,3 +28,25 @@ class AppBloc extends Bloc<AppEvent, AppState> {
     );
     );
   }
   }
 }
 }
+
+@freezed
+abstract class AppEvent with _$AppEvent {
+  const factory AppEvent.initial() = _Initial;
+  const factory AppEvent.viewsReceived(
+      Either<List<View>, WorkspaceError> appsOrFail) = ViewsReceived;
+}
+
+@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),
+      );
+}

+ 23 - 23
app_flowy/lib/home/application/app/app_bloc.freezed.dart

@@ -288,11 +288,11 @@ class _$AppStateTearOff {
 
 
   _AppState call(
   _AppState call(
       {required bool isLoading,
       {required bool isLoading,
-      required Option<List<App>> apps,
+      required Option<List<View>> views,
       required Either<Unit, WorkspaceError> successOrFailure}) {
       required Either<Unit, WorkspaceError> successOrFailure}) {
     return _AppState(
     return _AppState(
       isLoading: isLoading,
       isLoading: isLoading,
-      apps: apps,
+      views: views,
       successOrFailure: successOrFailure,
       successOrFailure: successOrFailure,
     );
     );
   }
   }
@@ -304,7 +304,7 @@ const $AppState = _$AppStateTearOff();
 /// @nodoc
 /// @nodoc
 mixin _$AppState {
 mixin _$AppState {
   bool get isLoading => throw _privateConstructorUsedError;
   bool get isLoading => throw _privateConstructorUsedError;
-  Option<List<App>> get apps => throw _privateConstructorUsedError;
+  Option<List<View>> get views => throw _privateConstructorUsedError;
   Either<Unit, WorkspaceError> get successOrFailure =>
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
       throw _privateConstructorUsedError;
 
 
@@ -319,7 +319,7 @@ abstract class $AppStateCopyWith<$Res> {
       _$AppStateCopyWithImpl<$Res>;
       _$AppStateCopyWithImpl<$Res>;
   $Res call(
   $Res call(
       {bool isLoading,
       {bool isLoading,
-      Option<List<App>> apps,
+      Option<List<View>> views,
       Either<Unit, WorkspaceError> successOrFailure});
       Either<Unit, WorkspaceError> successOrFailure});
 }
 }
 
 
@@ -334,7 +334,7 @@ class _$AppStateCopyWithImpl<$Res> implements $AppStateCopyWith<$Res> {
   @override
   @override
   $Res call({
   $Res call({
     Object? isLoading = freezed,
     Object? isLoading = freezed,
-    Object? apps = freezed,
+    Object? views = freezed,
     Object? successOrFailure = freezed,
     Object? successOrFailure = freezed,
   }) {
   }) {
     return _then(_value.copyWith(
     return _then(_value.copyWith(
@@ -342,10 +342,10 @@ class _$AppStateCopyWithImpl<$Res> implements $AppStateCopyWith<$Res> {
           ? _value.isLoading
           ? _value.isLoading
           : isLoading // ignore: cast_nullable_to_non_nullable
           : isLoading // ignore: cast_nullable_to_non_nullable
               as bool,
               as bool,
-      apps: apps == freezed
-          ? _value.apps
-          : apps // ignore: cast_nullable_to_non_nullable
-              as Option<List<App>>,
+      views: views == freezed
+          ? _value.views
+          : views // ignore: cast_nullable_to_non_nullable
+              as Option<List<View>>,
       successOrFailure: successOrFailure == freezed
       successOrFailure: successOrFailure == freezed
           ? _value.successOrFailure
           ? _value.successOrFailure
           : successOrFailure // ignore: cast_nullable_to_non_nullable
           : successOrFailure // ignore: cast_nullable_to_non_nullable
@@ -361,7 +361,7 @@ abstract class _$AppStateCopyWith<$Res> implements $AppStateCopyWith<$Res> {
   @override
   @override
   $Res call(
   $Res call(
       {bool isLoading,
       {bool isLoading,
-      Option<List<App>> apps,
+      Option<List<View>> views,
       Either<Unit, WorkspaceError> successOrFailure});
       Either<Unit, WorkspaceError> successOrFailure});
 }
 }
 
 
@@ -377,7 +377,7 @@ class __$AppStateCopyWithImpl<$Res> extends _$AppStateCopyWithImpl<$Res>
   @override
   @override
   $Res call({
   $Res call({
     Object? isLoading = freezed,
     Object? isLoading = freezed,
-    Object? apps = freezed,
+    Object? views = freezed,
     Object? successOrFailure = freezed,
     Object? successOrFailure = freezed,
   }) {
   }) {
     return _then(_AppState(
     return _then(_AppState(
@@ -385,10 +385,10 @@ class __$AppStateCopyWithImpl<$Res> extends _$AppStateCopyWithImpl<$Res>
           ? _value.isLoading
           ? _value.isLoading
           : isLoading // ignore: cast_nullable_to_non_nullable
           : isLoading // ignore: cast_nullable_to_non_nullable
               as bool,
               as bool,
-      apps: apps == freezed
-          ? _value.apps
-          : apps // ignore: cast_nullable_to_non_nullable
-              as Option<List<App>>,
+      views: views == freezed
+          ? _value.views
+          : views // ignore: cast_nullable_to_non_nullable
+              as Option<List<View>>,
       successOrFailure: successOrFailure == freezed
       successOrFailure: successOrFailure == freezed
           ? _value.successOrFailure
           ? _value.successOrFailure
           : successOrFailure // ignore: cast_nullable_to_non_nullable
           : successOrFailure // ignore: cast_nullable_to_non_nullable
@@ -402,19 +402,19 @@ class __$AppStateCopyWithImpl<$Res> extends _$AppStateCopyWithImpl<$Res>
 class _$_AppState implements _AppState {
 class _$_AppState implements _AppState {
   const _$_AppState(
   const _$_AppState(
       {required this.isLoading,
       {required this.isLoading,
-      required this.apps,
+      required this.views,
       required this.successOrFailure});
       required this.successOrFailure});
 
 
   @override
   @override
   final bool isLoading;
   final bool isLoading;
   @override
   @override
-  final Option<List<App>> apps;
+  final Option<List<View>> views;
   @override
   @override
   final Either<Unit, WorkspaceError> successOrFailure;
   final Either<Unit, WorkspaceError> successOrFailure;
 
 
   @override
   @override
   String toString() {
   String toString() {
-    return 'AppState(isLoading: $isLoading, apps: $apps, successOrFailure: $successOrFailure)';
+    return 'AppState(isLoading: $isLoading, views: $views, successOrFailure: $successOrFailure)';
   }
   }
 
 
   @override
   @override
@@ -424,8 +424,8 @@ class _$_AppState implements _AppState {
             (identical(other.isLoading, isLoading) ||
             (identical(other.isLoading, isLoading) ||
                 const DeepCollectionEquality()
                 const DeepCollectionEquality()
                     .equals(other.isLoading, isLoading)) &&
                     .equals(other.isLoading, isLoading)) &&
-            (identical(other.apps, apps) ||
-                const DeepCollectionEquality().equals(other.apps, apps)) &&
+            (identical(other.views, views) ||
+                const DeepCollectionEquality().equals(other.views, views)) &&
             (identical(other.successOrFailure, successOrFailure) ||
             (identical(other.successOrFailure, successOrFailure) ||
                 const DeepCollectionEquality()
                 const DeepCollectionEquality()
                     .equals(other.successOrFailure, successOrFailure)));
                     .equals(other.successOrFailure, successOrFailure)));
@@ -435,7 +435,7 @@ class _$_AppState implements _AppState {
   int get hashCode =>
   int get hashCode =>
       runtimeType.hashCode ^
       runtimeType.hashCode ^
       const DeepCollectionEquality().hash(isLoading) ^
       const DeepCollectionEquality().hash(isLoading) ^
-      const DeepCollectionEquality().hash(apps) ^
+      const DeepCollectionEquality().hash(views) ^
       const DeepCollectionEquality().hash(successOrFailure);
       const DeepCollectionEquality().hash(successOrFailure);
 
 
   @JsonKey(ignore: true)
   @JsonKey(ignore: true)
@@ -447,13 +447,13 @@ class _$_AppState implements _AppState {
 abstract class _AppState implements AppState {
 abstract class _AppState implements AppState {
   const factory _AppState(
   const factory _AppState(
       {required bool isLoading,
       {required bool isLoading,
-      required Option<List<App>> apps,
+      required Option<List<View>> views,
       required Either<Unit, WorkspaceError> successOrFailure}) = _$_AppState;
       required Either<Unit, WorkspaceError> successOrFailure}) = _$_AppState;
 
 
   @override
   @override
   bool get isLoading => throw _privateConstructorUsedError;
   bool get isLoading => throw _privateConstructorUsedError;
   @override
   @override
-  Option<List<App>> get apps => throw _privateConstructorUsedError;
+  Option<List<View>> get views => throw _privateConstructorUsedError;
   @override
   @override
   Either<Unit, WorkspaceError> get successOrFailure =>
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
       throw _privateConstructorUsedError;

+ 0 - 8
app_flowy/lib/home/application/app/app_event.dart

@@ -1,8 +0,0 @@
-part of 'app_bloc.dart';
-
-@freezed
-abstract class AppEvent with _$AppEvent {
-  const factory AppEvent.initial() = _Initial;
-  const factory AppEvent.viewsReceived(
-      Either<List<View>, WorkspaceError> appsOrFail) = ViewsReceived;
-}

+ 0 - 16
app_flowy/lib/home/application/app/app_state.dart

@@ -1,16 +0,0 @@
-part of 'app_bloc.dart';
-
-@freezed
-abstract class AppState implements _$AppState {
-  const factory AppState({
-    required bool isLoading,
-    required Option<List<App>> apps,
-    required Either<Unit, WorkspaceError> successOrFailure,
-  }) = _AppState;
-
-  factory AppState.initial() => AppState(
-        isLoading: false,
-        apps: none(),
-        successOrFailure: left(unit),
-      );
-}

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

@@ -0,0 +1,56 @@
+import 'package:app_flowy/home/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: (_) {
+      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/home/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;
+}

+ 31 - 3
app_flowy/lib/home/application/home_bloc.dart

@@ -4,9 +4,6 @@ import 'package:app_flowy/home/presentation/widgets/blank_page.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:dartz/dartz.dart';
 import 'package:dartz/dartz.dart';
-
-part 'home_event.dart';
-part 'home_state.dart';
 part 'home_bloc.freezed.dart';
 part 'home_bloc.freezed.dart';
 
 
 class HomeBloc extends Bloc<HomeEvent, HomeState> {
 class HomeBloc extends Bloc<HomeEvent, HomeState> {
@@ -40,3 +37,34 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
     return super.close();
     return super.close();
   }
   }
 }
 }
+
+@freezed
+abstract class HomeEvent with _$HomeEvent {
+  const factory HomeEvent.showLoading(bool isLoading) = _ShowLoading;
+  const factory HomeEvent.forceCollapse(bool forceCollapse) = _ForceCollapse;
+
+  //page
+  const factory HomeEvent.setPage(PageContext context) = SetCurrentPage;
+
+  //edit pannel
+  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 PageContext pageContext,
+    required Option<EditPannelContext> editContext,
+  }) = _HomeState;
+
+  factory HomeState.initial() => HomeState(
+        isLoading: false,
+        forceCollapse: false,
+        pageContext: const BlankPageContext(),
+        editContext: none(),
+      );
+}

+ 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.forceCollapse(bool forceCollapse) = _ForceCollapse;
-
-  //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 forceCollapse,
-    required PageContext pageContext,
-    required Option<EditPannelContext> editContext,
-  }) = _HomeState;
-
-  factory HomeState.initial() => HomeState(
-        isLoading: false,
-        forceCollapse: false,
-        pageContext: const BlankPageContext(),
-        editContext: none(),
-      );
-}

+ 33 - 17
app_flowy/lib/home/application/menu/menu_bloc.dart

@@ -7,8 +7,7 @@ import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-part 'menu_event.dart';
-part 'menu_state.dart';
+
 part 'menu_bloc.freezed.dart';
 part 'menu_bloc.freezed.dart';
 
 
 class MenuBloc extends Bloc<MenuEvent, MenuState> {
 class MenuBloc extends Bloc<MenuEvent, MenuState> {
@@ -21,12 +20,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
   ) async* {
   ) async* {
     yield* event.map(
     yield* event.map(
       initial: (value) async* {
       initial: (value) async* {
-        iWorkspaceImpl.startWatching(addAppCallback: (appsOrFail) {
-          appsOrFail.fold(
-            (apps) => add(MenuEvent.appsReceived(left(apps))),
-            (error) => add(MenuEvent.appsReceived(right(error))),
-          );
-        });
+        yield* _fetchApps();
       },
       },
       collapse: (e) async* {
       collapse: (e) async* {
         final isCollapse = state.isCollapse;
         final isCollapse = state.isCollapse;
@@ -38,12 +32,6 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
       createApp: (CreateApp event) async* {
       createApp: (CreateApp event) async* {
         yield* _performActionOnCreateApp(event);
         yield* _performActionOnCreateApp(event);
       },
       },
-      appsReceived: (AppsReceived value) async* {
-        yield value.appsOrFail.fold(
-          (apps) => state.copyWith(apps: some(apps)),
-          (error) => state.copyWith(successOrFailure: right(error)),
-        );
-      },
     );
     );
   }
   }
 
 
@@ -64,8 +52,36 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
     });
     });
   }
   }
 
 
-  @override
-  Future<void> close() {
-    return super.close();
+  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(PageContext context) = OpenPage;
+  const factory MenuEvent.createApp(String name, {String? desc}) = CreateApp;
+}
+
+@freezed
+abstract class MenuState implements _$MenuState {
+  const factory MenuState({
+    required bool isCollapse,
+    required Option<PageContext> pageContext,
+    required Option<List<App>> apps,
+    required Either<Unit, WorkspaceError> successOrFailure,
+  }) = _MenuState;
+
+  factory MenuState.initial() => MenuState(
+        isCollapse: false,
+        pageContext: none(),
+        apps: none(),
+        successOrFailure: left(unit),
+      );
+}

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

@@ -36,12 +36,6 @@ class _$MenuEventTearOff {
       desc: desc,
       desc: desc,
     );
     );
   }
   }
-
-  AppsReceived appsReceived(Either<List<App>, WorkspaceError> appsOrFail) {
-    return AppsReceived(
-      appsOrFail,
-    );
-  }
 }
 }
 
 
 /// @nodoc
 /// @nodoc
@@ -55,8 +49,6 @@ mixin _$MenuEvent {
     required TResult Function() collapse,
     required TResult Function() collapse,
     required TResult Function(PageContext context) openPage,
     required TResult Function(PageContext context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(String name, String? desc) createApp,
-    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
-        appsReceived,
   }) =>
   }) =>
       throw _privateConstructorUsedError;
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   @optionalTypeArgs
@@ -65,8 +57,6 @@ mixin _$MenuEvent {
     TResult Function()? collapse,
     TResult Function()? collapse,
     TResult Function(PageContext context)? openPage,
     TResult Function(PageContext context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(String name, String? desc)? createApp,
-    TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
-        appsReceived,
     required TResult orElse(),
     required TResult orElse(),
   }) =>
   }) =>
       throw _privateConstructorUsedError;
       throw _privateConstructorUsedError;
@@ -76,7 +66,6 @@ mixin _$MenuEvent {
     required TResult Function(Collapse value) collapse,
     required TResult Function(Collapse value) collapse,
     required TResult Function(OpenPage value) openPage,
     required TResult Function(OpenPage value) openPage,
     required TResult Function(CreateApp value) createApp,
     required TResult Function(CreateApp value) createApp,
-    required TResult Function(AppsReceived value) appsReceived,
   }) =>
   }) =>
       throw _privateConstructorUsedError;
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   @optionalTypeArgs
@@ -85,7 +74,6 @@ mixin _$MenuEvent {
     TResult Function(Collapse value)? collapse,
     TResult Function(Collapse value)? collapse,
     TResult Function(OpenPage value)? openPage,
     TResult Function(OpenPage value)? openPage,
     TResult Function(CreateApp value)? createApp,
     TResult Function(CreateApp value)? createApp,
-    TResult Function(AppsReceived value)? appsReceived,
     required TResult orElse(),
     required TResult orElse(),
   }) =>
   }) =>
       throw _privateConstructorUsedError;
       throw _privateConstructorUsedError;
@@ -147,8 +135,6 @@ class _$_Initial implements _Initial {
     required TResult Function() collapse,
     required TResult Function() collapse,
     required TResult Function(PageContext context) openPage,
     required TResult Function(PageContext context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(String name, String? desc) createApp,
-    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
-        appsReceived,
   }) {
   }) {
     return initial();
     return initial();
   }
   }
@@ -160,8 +146,6 @@ class _$_Initial implements _Initial {
     TResult Function()? collapse,
     TResult Function()? collapse,
     TResult Function(PageContext context)? openPage,
     TResult Function(PageContext context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(String name, String? desc)? createApp,
-    TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
-        appsReceived,
     required TResult orElse(),
     required TResult orElse(),
   }) {
   }) {
     if (initial != null) {
     if (initial != null) {
@@ -177,7 +161,6 @@ class _$_Initial implements _Initial {
     required TResult Function(Collapse value) collapse,
     required TResult Function(Collapse value) collapse,
     required TResult Function(OpenPage value) openPage,
     required TResult Function(OpenPage value) openPage,
     required TResult Function(CreateApp value) createApp,
     required TResult Function(CreateApp value) createApp,
-    required TResult Function(AppsReceived value) appsReceived,
   }) {
   }) {
     return initial(this);
     return initial(this);
   }
   }
@@ -189,7 +172,6 @@ class _$_Initial implements _Initial {
     TResult Function(Collapse value)? collapse,
     TResult Function(Collapse value)? collapse,
     TResult Function(OpenPage value)? openPage,
     TResult Function(OpenPage value)? openPage,
     TResult Function(CreateApp value)? createApp,
     TResult Function(CreateApp value)? createApp,
-    TResult Function(AppsReceived value)? appsReceived,
     required TResult orElse(),
     required TResult orElse(),
   }) {
   }) {
     if (initial != null) {
     if (initial != null) {
@@ -244,8 +226,6 @@ class _$Collapse implements Collapse {
     required TResult Function() collapse,
     required TResult Function() collapse,
     required TResult Function(PageContext context) openPage,
     required TResult Function(PageContext context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(String name, String? desc) createApp,
-    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
-        appsReceived,
   }) {
   }) {
     return collapse();
     return collapse();
   }
   }
@@ -257,8 +237,6 @@ class _$Collapse implements Collapse {
     TResult Function()? collapse,
     TResult Function()? collapse,
     TResult Function(PageContext context)? openPage,
     TResult Function(PageContext context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(String name, String? desc)? createApp,
-    TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
-        appsReceived,
     required TResult orElse(),
     required TResult orElse(),
   }) {
   }) {
     if (collapse != null) {
     if (collapse != null) {
@@ -274,7 +252,6 @@ class _$Collapse implements Collapse {
     required TResult Function(Collapse value) collapse,
     required TResult Function(Collapse value) collapse,
     required TResult Function(OpenPage value) openPage,
     required TResult Function(OpenPage value) openPage,
     required TResult Function(CreateApp value) createApp,
     required TResult Function(CreateApp value) createApp,
-    required TResult Function(AppsReceived value) appsReceived,
   }) {
   }) {
     return collapse(this);
     return collapse(this);
   }
   }
@@ -286,7 +263,6 @@ class _$Collapse implements Collapse {
     TResult Function(Collapse value)? collapse,
     TResult Function(Collapse value)? collapse,
     TResult Function(OpenPage value)? openPage,
     TResult Function(OpenPage value)? openPage,
     TResult Function(CreateApp value)? createApp,
     TResult Function(CreateApp value)? createApp,
-    TResult Function(AppsReceived value)? appsReceived,
     required TResult orElse(),
     required TResult orElse(),
   }) {
   }) {
     if (collapse != null) {
     if (collapse != null) {
@@ -366,8 +342,6 @@ class _$OpenPage implements OpenPage {
     required TResult Function() collapse,
     required TResult Function() collapse,
     required TResult Function(PageContext context) openPage,
     required TResult Function(PageContext context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(String name, String? desc) createApp,
-    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
-        appsReceived,
   }) {
   }) {
     return openPage(context);
     return openPage(context);
   }
   }
@@ -379,8 +353,6 @@ class _$OpenPage implements OpenPage {
     TResult Function()? collapse,
     TResult Function()? collapse,
     TResult Function(PageContext context)? openPage,
     TResult Function(PageContext context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(String name, String? desc)? createApp,
-    TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
-        appsReceived,
     required TResult orElse(),
     required TResult orElse(),
   }) {
   }) {
     if (openPage != null) {
     if (openPage != null) {
@@ -396,7 +368,6 @@ class _$OpenPage implements OpenPage {
     required TResult Function(Collapse value) collapse,
     required TResult Function(Collapse value) collapse,
     required TResult Function(OpenPage value) openPage,
     required TResult Function(OpenPage value) openPage,
     required TResult Function(CreateApp value) createApp,
     required TResult Function(CreateApp value) createApp,
-    required TResult Function(AppsReceived value) appsReceived,
   }) {
   }) {
     return openPage(this);
     return openPage(this);
   }
   }
@@ -408,7 +379,6 @@ class _$OpenPage implements OpenPage {
     TResult Function(Collapse value)? collapse,
     TResult Function(Collapse value)? collapse,
     TResult Function(OpenPage value)? openPage,
     TResult Function(OpenPage value)? openPage,
     TResult Function(CreateApp value)? createApp,
     TResult Function(CreateApp value)? createApp,
-    TResult Function(AppsReceived value)? appsReceived,
     required TResult orElse(),
     required TResult orElse(),
   }) {
   }) {
     if (openPage != null) {
     if (openPage != null) {
@@ -504,8 +474,6 @@ class _$CreateApp implements CreateApp {
     required TResult Function() collapse,
     required TResult Function() collapse,
     required TResult Function(PageContext context) openPage,
     required TResult Function(PageContext context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(String name, String? desc) createApp,
-    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
-        appsReceived,
   }) {
   }) {
     return createApp(name, desc);
     return createApp(name, desc);
   }
   }
@@ -517,8 +485,6 @@ class _$CreateApp implements CreateApp {
     TResult Function()? collapse,
     TResult Function()? collapse,
     TResult Function(PageContext context)? openPage,
     TResult Function(PageContext context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(String name, String? desc)? createApp,
-    TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
-        appsReceived,
     required TResult orElse(),
     required TResult orElse(),
   }) {
   }) {
     if (createApp != null) {
     if (createApp != null) {
@@ -534,7 +500,6 @@ class _$CreateApp implements CreateApp {
     required TResult Function(Collapse value) collapse,
     required TResult Function(Collapse value) collapse,
     required TResult Function(OpenPage value) openPage,
     required TResult Function(OpenPage value) openPage,
     required TResult Function(CreateApp value) createApp,
     required TResult Function(CreateApp value) createApp,
-    required TResult Function(AppsReceived value) appsReceived,
   }) {
   }) {
     return createApp(this);
     return createApp(this);
   }
   }
@@ -546,7 +511,6 @@ class _$CreateApp implements CreateApp {
     TResult Function(Collapse value)? collapse,
     TResult Function(Collapse value)? collapse,
     TResult Function(OpenPage value)? openPage,
     TResult Function(OpenPage value)? openPage,
     TResult Function(CreateApp value)? createApp,
     TResult Function(CreateApp value)? createApp,
-    TResult Function(AppsReceived value)? appsReceived,
     required TResult orElse(),
     required TResult orElse(),
   }) {
   }) {
     if (createApp != null) {
     if (createApp != null) {
@@ -566,138 +530,6 @@ abstract class CreateApp implements MenuEvent {
       throw _privateConstructorUsedError;
       throw _privateConstructorUsedError;
 }
 }
 
 
-/// @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 _$MenuEventCopyWithImpl<$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 'MenuEvent.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() initial,
-    required TResult Function() collapse,
-    required TResult Function(PageContext context) openPage,
-    required TResult Function(String name, String? desc) createApp,
-    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
-        appsReceived,
-  }) {
-    return appsReceived(appsOrFail);
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeWhen<TResult extends Object?>({
-    TResult Function()? initial,
-    TResult Function()? collapse,
-    TResult Function(PageContext context)? openPage,
-    TResult Function(String name, String? desc)? createApp,
-    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(_Initial value) initial,
-    required TResult Function(Collapse value) collapse,
-    required TResult Function(OpenPage value) openPage,
-    required TResult Function(CreateApp value) createApp,
-    required TResult Function(AppsReceived value) appsReceived,
-  }) {
-    return appsReceived(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,
-    TResult Function(AppsReceived value)? appsReceived,
-    required TResult orElse(),
-  }) {
-    if (appsReceived != null) {
-      return appsReceived(this);
-    }
-    return orElse();
-  }
-}
-
-abstract class AppsReceived implements MenuEvent {
-  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
 /// @nodoc
 class _$MenuStateTearOff {
 class _$MenuStateTearOff {
   const _$MenuStateTearOff();
   const _$MenuStateTearOff();

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

@@ -1,11 +0,0 @@
-part of 'menu_bloc.dart';
-
-@freezed
-abstract class MenuEvent with _$MenuEvent {
-  const factory MenuEvent.initial() = _Initial;
-  const factory MenuEvent.collapse() = Collapse;
-  const factory MenuEvent.openPage(PageContext context) = OpenPage;
-  const factory MenuEvent.createApp(String name, {String? desc}) = CreateApp;
-  const factory MenuEvent.appsReceived(
-      Either<List<App>, WorkspaceError> appsOrFail) = AppsReceived;
-}

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

@@ -1,18 +0,0 @@
-part of 'menu_bloc.dart';
-
-@freezed
-abstract class MenuState implements _$MenuState {
-  const factory MenuState({
-    required bool isCollapse,
-    required Option<PageContext> pageContext,
-    required Option<List<App>> apps,
-    required Either<Unit, WorkspaceError> successOrFailure,
-  }) = _MenuState;
-
-  factory MenuState.initial() => MenuState(
-        isCollapse: false,
-        pageContext: none(),
-        apps: none(),
-        successOrFailure: left(unit),
-      );
-}

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

@@ -0,0 +1,64 @@
+import 'package:app_flowy/home/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/home/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;
+}

+ 2 - 0
app_flowy/lib/home/domain/i_app.dart

@@ -13,7 +13,9 @@ abstract class IApp {
       required String name,
       required String name,
       String? desc,
       String? desc,
       required ViewType viewType});
       required ViewType viewType});
+}
 
 
+abstract class IAppWatch {
   void startWatching(
   void startWatching(
       {AppAddViewCallback? addViewCallback,
       {AppAddViewCallback? addViewCallback,
       AppUpdatedCallback? updatedCallback});
       AppUpdatedCallback? updatedCallback});

+ 3 - 2
app_flowy/lib/home/domain/i_workspace.dart

@@ -9,9 +9,10 @@ abstract class IWorkspace {
   Future<Either<App, WorkspaceError>> createApp(
   Future<Either<App, WorkspaceError>> createApp(
       {required String name, String? desc});
       {required String name, String? desc});
 
 
-  Future<Either<List<App>, WorkspaceError>> getApps(
-      {required String workspaceId});
+  Future<Either<List<App>, WorkspaceError>> getApps();
+}
 
 
+abstract class IWorkspaceWatch {
   void startWatching(
   void startWatching(
       {WorkspaceAddAppCallback? addAppCallback,
       {WorkspaceAddAppCallback? addAppCallback,
       WorkspaceUpdatedCallback? updatedCallback});
       WorkspaceUpdatedCallback? updatedCallback});

+ 20 - 6
app_flowy/lib/home/infrastructure/deps_resolver.dart

@@ -1,4 +1,5 @@
 import 'package:app_flowy/home/application/menu/menu_bloc.dart';
 import 'package:app_flowy/home/application/menu/menu_bloc.dart';
+import 'package:app_flowy/home/application/menu/menu_watch.dart';
 import 'package:app_flowy/home/infrastructure/i_app_impl.dart';
 import 'package:app_flowy/home/infrastructure/i_app_impl.dart';
 import 'package:app_flowy/home/infrastructure/i_workspace_impl.dart';
 import 'package:app_flowy/home/infrastructure/i_workspace_impl.dart';
 import 'package:app_flowy/home/infrastructure/repos/app_repo.dart';
 import 'package:app_flowy/home/infrastructure/repos/app_repo.dart';
@@ -7,21 +8,34 @@ import 'package:get_it/get_it.dart';
 
 
 class HomeDepsResolver {
 class HomeDepsResolver {
   static Future<void> resolve(GetIt getIt) async {
   static Future<void> resolve(GetIt getIt) async {
-    getIt.registerFactoryParam<WorkspaceRepository, String, void>(
-        (workspaceId, _) => WorkspaceRepository(workspaceId: workspaceId));
-
+    //App
     getIt.registerFactoryParam<AppRepository, String, void>(
     getIt.registerFactoryParam<AppRepository, String, void>(
         (appId, _) => AppRepository(appId: appId));
         (appId, _) => AppRepository(appId: appId));
-
-    //Interface implementation
+    getIt.registerFactoryParam<AppWatchRepository, String, void>(
+        (appId, _) => AppWatchRepository(appId: appId));
     getIt.registerFactoryParam<IApp, String, void>(
     getIt.registerFactoryParam<IApp, String, void>(
         (appId, _) => IAppImpl(repo: getIt<AppRepository>(param1: appId)));
         (appId, _) => IAppImpl(repo: getIt<AppRepository>(param1: appId)));
+    getIt.registerFactoryParam<IAppWatch, String, void>((appId, _) =>
+        IAppWatchImpl(repo: getIt<AppWatchRepository>(param1: appId)));
+
+    //workspace
+    getIt.registerFactoryParam<WorkspaceRepo, String, void>(
+        (workspaceId, _) => WorkspaceRepo(workspaceId: workspaceId));
+    getIt.registerFactoryParam<WorkspaceWatchRepo, String, void>(
+        (workspaceId, _) => WorkspaceWatchRepo(workspaceId: workspaceId));
 
 
     getIt.registerFactoryParam<IWorkspace, String, void>((workspacId, _) =>
     getIt.registerFactoryParam<IWorkspace, String, void>((workspacId, _) =>
-        IWorkspaceImpl(repo: getIt<WorkspaceRepository>(param1: workspacId)));
+        IWorkspaceImpl(repo: getIt<WorkspaceRepo>(param1: workspacId)));
+    getIt.registerFactoryParam<IWorkspaceWatch, String, void>((workspacId, _) =>
+        IWorkspaceWatchImpl(
+            repo: getIt<WorkspaceWatchRepo>(param1: workspacId)));
 
 
     //Bloc
     //Bloc
     getIt.registerFactoryParam<MenuBloc, String, void>(
     getIt.registerFactoryParam<MenuBloc, String, void>(
         (workspaceId, _) => MenuBloc(getIt<IWorkspace>(param1: workspaceId)));
         (workspaceId, _) => MenuBloc(getIt<IWorkspace>(param1: workspaceId)));
+    getIt.registerFactoryParam<MenuWatchBloc, String, void>((workspaceId, _) =>
+        MenuWatchBloc(getIt<IWorkspaceWatch>(param1: workspaceId)));
+
+    // AppWatchBloc
   }
   }
 }
 }

+ 7 - 0
app_flowy/lib/home/infrastructure/i_app_impl.dart

@@ -25,6 +25,13 @@ class IAppImpl extends IApp {
       required ViewType viewType}) {
       required ViewType viewType}) {
     return repo.createView(appId, name, desc ?? "", viewType);
     return repo.createView(appId, name, desc ?? "", viewType);
   }
   }
+}
+
+class IAppWatchImpl extends IAppWatch {
+  AppWatchRepository repo;
+  IAppWatchImpl({
+    required this.repo,
+  });
 
 
   @override
   @override
   void startWatching(
   void startWatching(

+ 10 - 6
app_flowy/lib/home/infrastructure/i_workspace_impl.dart

@@ -7,7 +7,7 @@ import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 export 'package:app_flowy/home/domain/i_workspace.dart';
 export 'package:app_flowy/home/domain/i_workspace.dart';
 
 
 class IWorkspaceImpl extends IWorkspace {
 class IWorkspaceImpl extends IWorkspace {
-  WorkspaceRepository repo;
+  WorkspaceRepo repo;
   IWorkspaceImpl({
   IWorkspaceImpl({
     required this.repo,
     required this.repo,
   });
   });
@@ -19,17 +19,21 @@ class IWorkspaceImpl extends IWorkspace {
   }
   }
 
 
   @override
   @override
-  Future<Either<List<App>, WorkspaceError>> getApps(
-      {required String workspaceId}) {
-    return repo
-        .getWorkspace(workspaceId: workspaceId, readApps: true)
-        .then((result) {
+  Future<Either<List<App>, WorkspaceError>> getApps() {
+    return repo.getWorkspace(readApps: true).then((result) {
       return result.fold(
       return result.fold(
         (workspace) => left(workspace.apps.items),
         (workspace) => left(workspace.apps.items),
         (error) => right(error),
         (error) => right(error),
       );
       );
     });
     });
   }
   }
+}
+
+class IWorkspaceWatchImpl extends IWorkspaceWatch {
+  WorkspaceWatchRepo repo;
+  IWorkspaceWatchImpl({
+    required this.repo,
+  });
 
 
   @override
   @override
   void startWatching(
   void startWatching(

+ 15 - 5
app_flowy/lib/home/infrastructure/repos/app_repo.dart

@@ -13,9 +13,6 @@ import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pbenum.dart';
 import 'package:flowy_sdk/rust_stream.dart';
 import 'package:flowy_sdk/rust_stream.dart';
 
 
 class AppRepository {
 class AppRepository {
-  StreamSubscription<ObservableSubject>? _subscription;
-  AppAddViewCallback? _addViewCallback;
-  AppUpdatedCallback? _updatedCallback;
   String appId;
   String appId;
   AppRepository({
   AppRepository({
     required this.appId,
     required this.appId,
@@ -52,6 +49,19 @@ class AppRepository {
       );
       );
     });
     });
   }
   }
+}
+
+class AppWatchRepository {
+  StreamSubscription<ObservableSubject>? _subscription;
+  AppAddViewCallback? _addViewCallback;
+  AppUpdatedCallback? _updatedCallback;
+  String appId;
+  late AppRepository _repo;
+  AppWatchRepository({
+    required this.appId,
+  }) {
+    _repo = AppRepository(appId: appId);
+  }
 
 
   void startWatching(
   void startWatching(
       {AppAddViewCallback? addViewCallback,
       {AppAddViewCallback? addViewCallback,
@@ -76,7 +86,7 @@ class AppRepository {
         if (_addViewCallback == null) {
         if (_addViewCallback == null) {
           return;
           return;
         }
         }
-        getViews(appId: appId).then((result) {
+        _repo.getViews(appId: appId).then((result) {
           result.fold(
           result.fold(
             (views) => _addViewCallback!(left(views)),
             (views) => _addViewCallback!(left(views)),
             (error) => _addViewCallback!(right(error)),
             (error) => _addViewCallback!(right(error)),
@@ -87,7 +97,7 @@ class AppRepository {
         if (_updatedCallback == null) {
         if (_updatedCallback == null) {
           return;
           return;
         }
         }
-        getAppDesc().then((result) {
+        _repo.getAppDesc().then((result) {
           result.fold(
           result.fold(
             (app) => _updatedCallback!(app.name, app.desc),
             (app) => _updatedCallback!(app.name, app.desc),
             (error) => Log.error(error),
             (error) => Log.error(error),

+ 19 - 8
app_flowy/lib/home/infrastructure/repos/workspace_repo.dart

@@ -12,12 +12,9 @@ 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/protobuf/flowy-workspace/workspace_query.pb.dart';
 import 'package:flowy_sdk/rust_stream.dart';
 import 'package:flowy_sdk/rust_stream.dart';
 
 
-class WorkspaceRepository {
-  StreamSubscription<ObservableSubject>? _subscription;
-  WorkspaceAddAppCallback? _addAppCallback;
-  WorkspaceUpdatedCallback? _updatedCallback;
+class WorkspaceRepo {
   String workspaceId;
   String workspaceId;
-  WorkspaceRepository({
+  WorkspaceRepo({
     required this.workspaceId,
     required this.workspaceId,
   });
   });
 
 
@@ -39,7 +36,7 @@ class WorkspaceRepository {
   }
   }
 
 
   Future<Either<Workspace, WorkspaceError>> getWorkspace(
   Future<Either<Workspace, WorkspaceError>> getWorkspace(
-      {required String workspaceId, bool readApps = false}) {
+      {bool readApps = false}) {
     final request = QueryWorkspaceRequest.create()
     final request = QueryWorkspaceRequest.create()
       ..workspaceId = workspaceId
       ..workspaceId = workspaceId
       ..readApps = readApps;
       ..readApps = readApps;
@@ -51,6 +48,20 @@ class WorkspaceRepository {
       );
       );
     });
     });
   }
   }
+}
+
+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(
   void startWatching(
       {WorkspaceAddAppCallback? addAppCallback,
       {WorkspaceAddAppCallback? addAppCallback,
@@ -76,7 +87,7 @@ class WorkspaceRepository {
         if (_updatedCallback == null) {
         if (_updatedCallback == null) {
           return;
           return;
         }
         }
-        getWorkspace(workspaceId: workspaceId).then((result) {
+        _repo.getWorkspace().then((result) {
           result.fold(
           result.fold(
             (workspace) => _updatedCallback!(workspace.name, workspace.desc),
             (workspace) => _updatedCallback!(workspace.name, workspace.desc),
             (error) => Log.error(error),
             (error) => Log.error(error),
@@ -87,7 +98,7 @@ class WorkspaceRepository {
         if (_addAppCallback == null) {
         if (_addAppCallback == null) {
           return;
           return;
         }
         }
-        getWorkspace(workspaceId: workspaceId, readApps: true).then((result) {
+        _repo.getWorkspace(readApps: true).then((result) {
           result.fold(
           result.fold(
             (workspace) => _addAppCallback!(left(workspace.apps.items)),
             (workspace) => _addAppCallback!(left(workspace.apps.items)),
             (error) => _addAppCallback!(right(error)),
             (error) => _addAppCallback!(right(error)),

+ 49 - 30
app_flowy/lib/home/presentation/widgets/app/app_list_widget.dart

@@ -10,39 +10,44 @@ import 'package:flowy_infra_ui/widget/error_page.dart';
 
 
 import 'app_widget.dart';
 import 'app_widget.dart';
 
 
+// class AppList extends StatelessWidget {
+//   const AppList({Key? key}) : super(key: key);
+//   @override
+//   Widget build(BuildContext context) {
+//     return MultiBlocProvider(
+//       providers: [
+//         BlocProvider(
+//           create: (context) => getIt<AppBloc>()..add(const AppEvent.initial()),
+//         ),
+//       ],
+//       child: BlocBuilder<AppBloc, AppState>(
+//         buildWhen: (p, c) => p.apps != c.apps,
+//         builder: (context, state) {
+//           Log.info('AppList build');
+//           if (state.isLoading) {
+//             return const Center(
+//               child: CircularProgressIndicator.adaptive(),
+//             );
+//           }
+
+//           return state.apps.fold(
+//             () => state.successOrFailure.fold(
+//               (_) => const Text('You have no apps, create one?'),
+//               (error) => FlowyErrorPage(error.toString()),
+//             ),
+//             (apps) => _buildBody(apps),
+//           );
+//         },
+//       ),
+//     );
+//   }
+
 class AppList extends StatelessWidget {
 class AppList extends StatelessWidget {
-  const AppList({Key? key}) : super(key: key);
+  final List<App> apps;
+  const AppList({required this.apps, Key? key}) : super(key: key);
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    return MultiBlocProvider(
-      providers: [
-        BlocProvider(
-          create: (context) => getIt<AppBloc>()..add(const AppEvent.initial()),
-        ),
-      ],
-      child: BlocBuilder<AppBloc, AppState>(
-        buildWhen: (p, c) => p.apps != c.apps,
-        builder: (context, state) {
-          Log.info('AppList build');
-          if (state.isLoading) {
-            return const Center(
-              child: CircularProgressIndicator.adaptive(),
-            );
-          }
-
-          return state.apps.fold(
-            () => state.successOrFailure.fold(
-              (_) => const Text('You have no apps, create one?'),
-              (error) => FlowyErrorPage(error.toString()),
-            ),
-            (apps) => _buildBody(apps),
-          );
-        },
-      ),
-    );
-  }
-
-  Widget _buildBody(List<App> apps) {
     return ExpandableTheme(
     return ExpandableTheme(
         data: const ExpandableThemeData(
         data: const ExpandableThemeData(
           iconColor: Colors.blue,
           iconColor: Colors.blue,
@@ -55,4 +60,18 @@ class AppList extends StatelessWidget {
           ),
           ),
         ));
         ));
   }
   }
+
+  // Widget _buildBody(List<App> 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(),
+  //         ),
+  //       ));
+  // }
 }
 }

+ 21 - 2
app_flowy/lib/home/presentation/widgets/app/app_widget.dart

@@ -1,7 +1,9 @@
-// ignore: import_of_legacy_library_into_null_safe
-import 'package:app_flowy/home/presentation/widgets/menu/hom_menu_size.dart';
+import 'package:app_flowy/home/application/app/app_bloc.dart';
+import 'package:app_flowy/home/presentation/widgets/menu/menu_size.dart';
+import 'package:app_flowy/startup/startup.dart';
 import 'package:expandable/expandable.dart';
 import 'package:expandable/expandable.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/size.dart';
+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/app_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
@@ -13,6 +15,22 @@ class AppWidget extends StatelessWidget {
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
+    // return MultiBlocProvider(
+    //   providers: [
+    //     BlocProvider<AppBloc>(create: (context) => getIt<AppBloc>()),
+    //   ],
+    //   child: BlocBuilder<AppBloc, AppState>(
+    //     builder: (context, state) {
+    //       // final child = state.map(
+    //       //   initial: (_) => const CircularProgressIndicator.adaptive(),
+    //       //   loadViews: (s) => ViewList(s.views),
+    //       //   successOrFailure: (s) => FlowyErrorPage(s.error),
+    //       // );
+
+    //       return expandableWrapper(context, Container());
+    //     },
+    //   ),
+    // );
     return Container();
     return Container();
   }
   }
 
 
@@ -38,6 +56,7 @@ class AppWidget extends StatelessWidget {
                   padding: EdgeInsets.only(left: Sizes.iconMed),
                   padding: EdgeInsets.only(left: Sizes.iconMed),
                   child: child,
                   child: child,
                 ),
                 ),
+                collapsed: const Text("close"),
               ),
               ),
             ],
             ],
           ),
           ),

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

@@ -0,0 +1,29 @@
+import 'package:expandable/expandable.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
+import 'package:app_flowy/home/presentation/widgets/app/app_widget.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 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(),
+            ),
+          ));
+    });
+  }
+}

+ 33 - 11
app_flowy/lib/home/presentation/widgets/menu/home_menu.dart → app_flowy/lib/home/presentation/widgets/menu/menu.dart

@@ -1,7 +1,9 @@
 import 'package:app_flowy/home/application/menu/menu_bloc.dart';
 import 'package:app_flowy/home/application/menu/menu_bloc.dart';
+import 'package:app_flowy/home/application/menu/menu_watch.dart';
 import 'package:app_flowy/home/domain/page_context.dart';
 import 'package:app_flowy/home/domain/page_context.dart';
+import 'package:app_flowy/home/presentation/home_sizes.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/startup/tasks/app_widget_task.dart';
+import 'package:app_flowy/startup/tasks/application_task.dart';
 import 'package:dartz/dartz.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/text_style.dart';
 import 'package:flowy_infra/text_style.dart';
@@ -9,12 +11,13 @@ import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/styled_text_input.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/buttons/ok_cancel_button.dart';
 import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
 import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
+import 'package:flowy_infra_ui/widget/error_page.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import '../../home_sizes.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'package:textstyle_extensions/textstyle_extensions.dart';
 import 'package:textstyle_extensions/textstyle_extensions.dart';
+import 'app_list.dart';
 
 
 class HomeMenu extends StatelessWidget {
 class HomeMenu extends StatelessWidget {
   final Function(Option<PageContext>) pageContextChanged;
   final Function(Option<PageContext>) pageContextChanged;
@@ -30,9 +33,15 @@ class HomeMenu extends StatelessWidget {
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    return BlocProvider(
-      create: (context) =>
-          getIt<MenuBloc>(param1: workspaceId)..add(const MenuEvent.initial()),
+    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(
       child: MultiBlocListener(
         listeners: [
         listeners: [
           BlocListener<MenuBloc, MenuState>(
           BlocListener<MenuBloc, MenuState>(
@@ -58,16 +67,29 @@ class HomeMenu extends StatelessWidget {
         mainAxisAlignment: MainAxisAlignment.start,
         mainAxisAlignment: MainAxisAlignment.start,
         children: [
         children: [
           const MenuTopBar(),
           const MenuTopBar(),
-          Expanded(child: Container()),
-          NewAppButton(
-            createAppCallback: (appName) => context
-                .read<MenuBloc>()
-                .add(MenuEvent.createApp(appName, desc: "")),
-          ),
+          _renderAppList(context),
+          _renderNewButton(context),
         ],
         ],
       ).padding(horizontal: Insets.sm),
       ).padding(horizontal: Insets.sm),
     );
     );
   }
   }
+
+  Widget _renderAppList(BuildContext context) {
+    return BlocBuilder<MenuWatchBloc, MenuWatchState>(
+      builder: (context, state) => state.map(
+        initial: (_) => AppList(apps: context.read<MenuBloc>().state.apps),
+        loadApps: (event) => AppList(apps: some(event.apps)),
+        loadFail: (error) => FlowyErrorPage(error.toString()),
+      ),
+    );
+  }
+
+  Widget _renderNewButton(BuildContext context) {
+    return NewAppButton(
+      createAppCallback: (appName) =>
+          context.read<MenuBloc>().add(MenuEvent.createApp(appName, desc: "")),
+    );
+  }
 }
 }
 
 
 class MenuTopBar extends StatelessWidget {
 class MenuTopBar extends StatelessWidget {

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


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

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

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

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

+ 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
   @override
   Future<void> initialize(LaunchContext context) {
   Future<void> initialize(LaunchContext context) {
     final widget = context.getIt<AppFactory>().create();
     final widget = context.getIt<AppFactory>().create();
-    final app = AppWidget(child: widget);
+    final app = ApplicationWidget(child: widget);
     runApp(app);
     runApp(app);
 
 
     return Future(() => {});
     return Future(() => {});
   }
   }
 }
 }
 
 
-class AppWidget extends StatelessWidget {
+class ApplicationWidget extends StatelessWidget {
   final Widget child;
   final Widget child;
-  const AppWidget({
+  const ApplicationWidget({
     Key? key,
     Key? key,
     required this.child,
     required this.child,
   }) : super(key: key);
   }) : 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';
 export 'rust_sdk_init_task.dart';

+ 4 - 4
app_flowy/packages/flowy_infra_ui/example/pubspec.lock

@@ -14,7 +14,7 @@ packages:
       name: async
       name: async
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
-    version: "2.6.1"
+    version: "2.7.0"
   boolean_selector:
   boolean_selector:
     dependency: transitive
     dependency: transitive
     description:
     description:
@@ -35,7 +35,7 @@ packages:
       name: charcode
       name: charcode
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
-    version: "1.2.0"
+    version: "1.3.1"
   clock:
   clock:
     dependency: transitive
     dependency: transitive
     description:
     description:
@@ -176,7 +176,7 @@ packages:
       name: meta
       name: meta
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
-    version: "1.3.0"
+    version: "1.7.0"
   nested:
   nested:
     dependency: transitive
     dependency: transitive
     description:
     description:
@@ -258,7 +258,7 @@ packages:
       name: test_api
       name: test_api
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
-    version: "0.3.0"
+    version: "0.4.1"
   textstyle_extensions:
   textstyle_extensions:
     dependency: transitive
     dependency: transitive
     description:
     description:

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

@@ -14,7 +14,7 @@ packages:
       name: async
       name: async
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
-    version: "2.6.1"
+    version: "2.7.0"
   boolean_selector:
   boolean_selector:
     dependency: transitive
     dependency: transitive
     description:
     description:
@@ -35,7 +35,7 @@ packages:
       name: charcode
       name: charcode
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
-    version: "1.2.0"
+    version: "1.3.1"
   clock:
   clock:
     dependency: transitive
     dependency: transitive
     description:
     description:
@@ -162,7 +162,7 @@ packages:
       name: meta
       name: meta
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
-    version: "1.3.0"
+    version: "1.7.0"
   nested:
   nested:
     dependency: transitive
     dependency: transitive
     description:
     description:
@@ -244,7 +244,7 @@ packages:
       name: test_api
       name: test_api
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
-    version: "0.3.0"
+    version: "0.4.1"
   textstyle_extensions:
   textstyle_extensions:
     dependency: "direct main"
     dependency: "direct main"
     description:
     description:

+ 1 - 1
app_flowy/pubspec.lock

@@ -203,7 +203,7 @@ packages:
       name: expandable
       name: expandable
       url: "https://pub.dartlang.org"
       url: "https://pub.dartlang.org"
     source: hosted
     source: hosted
-    version: "4.1.4"
+    version: "5.0.1"
   fake_async:
   fake_async:
     dependency: transitive
     dependency: transitive
     description:
     description:

+ 1 - 1
app_flowy/pubspec.yaml

@@ -52,7 +52,7 @@ dependencies:
       ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
       ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
   sized_context: ^1.0.0+1
   sized_context: ^1.0.0+1
   styled_widget: '>=0.3.1'
   styled_widget: '>=0.3.1'
-  expandable: ^4.1.4
+  expandable: ^5.0.1
 
 
 
 
   # The following adds the Cupertino Icons font to your application.
   # The following adds the Cupertino Icons font to your application.

+ 4 - 4
rust-lib/dart-ffi/Cargo.toml

@@ -7,11 +7,11 @@ edition = "2018"
 [lib]
 [lib]
 name = "dart_ffi"
 name = "dart_ffi"
 # this value will change depending on the target os
 # this value will change depending on the target os
-# for iOS it would be `rlib`
-# for Macos it would be `rlib`
+# for iOS it would be `cdylib`
+# for Macos it would be `cdylib`
 # for android it would be `c-dylib`
 # for android it would be `c-dylib`
-# default rlib
-crate-type = ["rlib"]
+# default cdylib
+crate-type = ["cdylib"]
 
 
 
 
 [dependencies]
 [dependencies]