Browse Source

test stream from rust to flutter

appflowy 3 years ago
parent
commit
ebc489738c
52 changed files with 1153 additions and 407 deletions
  1. 14 1
      app_flowy/lib/home/application/app/app_bloc.dart
  2. 173 70
      app_flowy/lib/home/application/app/app_bloc.freezed.dart
  3. 3 2
      app_flowy/lib/home/application/app/app_event.dart
  4. 2 0
      app_flowy/lib/home/application/app/app_state.dart
  5. 19 4
      app_flowy/lib/home/application/menu/menu_bloc.dart
  6. 358 56
      app_flowy/lib/home/application/menu/menu_bloc.freezed.dart
  7. 5 2
      app_flowy/lib/home/application/menu/menu_event.dart
  8. 2 0
      app_flowy/lib/home/application/menu/menu_state.dart
  9. 11 1
      app_flowy/lib/home/domain/i_app.dart
  10. 10 0
      app_flowy/lib/home/domain/i_workspace.dart
  11. 7 4
      app_flowy/lib/home/infrastructure/deps_resolver.dart
  12. 14 1
      app_flowy/lib/home/infrastructure/i_app_impl.dart
  13. 21 1
      app_flowy/lib/home/infrastructure/i_workspace_impl.dart
  14. 17 19
      app_flowy/lib/home/infrastructure/repos/app_repo.dart
  15. 31 15
      app_flowy/lib/home/infrastructure/repos/workspace_repo.dart
  16. 1 0
      app_flowy/lib/home/presentation/home_screen.dart
  17. 58 0
      app_flowy/lib/home/presentation/widgets/app/app_list_widget.dart
  18. 122 0
      app_flowy/lib/home/presentation/widgets/app/app_widget.dart
  19. 0 0
      app_flowy/lib/home/presentation/widgets/app/new_app.dart
  20. 0 0
      app_flowy/lib/home/presentation/widgets/app/view_list.dart
  21. 5 2
      app_flowy/lib/home/presentation/widgets/menu/home_menu.dart
  22. 1 1
      app_flowy/lib/main.dart
  23. 1 1
      app_flowy/lib/startup/startup.dart
  24. 13 0
      app_flowy/packages/flowy_infra_ui/lib/widget/error_page.dart
  25. 16 14
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbenum.dart
  26. 10 9
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbjson.dart
  27. 8 8
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pb.dart
  28. 6 6
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pbenum.dart
  29. 9 9
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pbjson.dart
  30. 5 5
      app_flowy/pubspec.lock
  31. 6 1
      rust-lib/dart-ffi/src/model/ffi_response.rs
  32. 2 2
      rust-lib/flowy-derive/src/derive_cache/derive_cache.rs
  33. 0 1
      rust-lib/flowy-derive/src/proto_buf/deserialize.rs
  34. 10 1
      rust-lib/flowy-dispatch/src/dispatch.rs
  35. 1 1
      rust-lib/flowy-observable/src/dart/stream_sender.rs
  36. 2 2
      rust-lib/flowy-sdk/Cargo.toml
  37. 1 0
      rust-lib/flowy-workspace/Cargo.toml
  38. 4 4
      rust-lib/flowy-workspace/src/entities/view/parser/view_type.rs
  39. 7 7
      rust-lib/flowy-workspace/src/entities/view/view_create.rs
  40. 6 6
      rust-lib/flowy-workspace/src/event.rs
  41. 2 0
      rust-lib/flowy-workspace/src/handlers/app_handler.rs
  42. 3 0
      rust-lib/flowy-workspace/src/handlers/workspace_handler.rs
  43. 16 12
      rust-lib/flowy-workspace/src/observable/observable.rs
  44. 45 39
      rust-lib/flowy-workspace/src/protobuf/model/observable.rs
  45. 77 77
      rust-lib/flowy-workspace/src/protobuf/model/view_create.rs
  46. 6 5
      rust-lib/flowy-workspace/src/protobuf/proto/observable.proto
  47. 3 3
      rust-lib/flowy-workspace/src/protobuf/proto/view_create.proto
  48. 2 2
      rust-lib/flowy-workspace/src/services/app_controller.rs
  49. 2 0
      rust-lib/flowy-workspace/src/services/view_controller.rs
  50. 3 0
      rust-lib/flowy-workspace/src/services/workspace_controller.rs
  51. 11 11
      rust-lib/flowy-workspace/src/sql_tables/view/view_table.rs
  52. 2 2
      rust-lib/flowy-workspace/tests/event/app_test.rs

+ 14 - 1
app_flowy/lib/home/application/app/app_bloc.dart

@@ -1,6 +1,7 @@
 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/view_create.pb.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:dartz/dartz.dart';
@@ -16,5 +17,17 @@ class AppBloc extends Bloc<AppEvent, AppState> {
   @override
   Stream<AppState> mapEventToState(
     AppEvent event,
-  ) async* {}
+  ) async* {
+    yield* event.map(
+      initial: (e) async* {
+        iAppImpl.startWatching(
+          updatedCallback: (name, desc) {},
+          addViewCallback: (views) {},
+        );
+      },
+      viewsReceived: (e) async* {
+        yield state;
+      },
+    );
+  }
 }

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

@@ -16,8 +16,12 @@ final _privateConstructorUsedError = UnsupportedError(
 class _$AppEventTearOff {
   const _$AppEventTearOff();
 
-  AppsReceived appsReceived(Either<List<App>, WorkspaceError> appsOrFail) {
-    return AppsReceived(
+  _Initial initial() {
+    return const _Initial();
+  }
+
+  ViewsReceived viewsReceived(Either<List<View>, WorkspaceError> appsOrFail) {
+    return ViewsReceived(
       appsOrFail,
     );
   }
@@ -28,44 +32,40 @@ const $AppEvent = _$AppEventTearOff();
 
 /// @nodoc
 mixin _$AppEvent {
-  Either<List<App>, WorkspaceError> get appsOrFail =>
-      throw _privateConstructorUsedError;
-
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
-    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
-        appsReceived,
+    required TResult Function() initial,
+    required TResult Function(Either<List<View>, WorkspaceError> appsOrFail)
+        viewsReceived,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
-    TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
-        appsReceived,
+    TResult Function()? initial,
+    TResult Function(Either<List<View>, WorkspaceError> appsOrFail)?
+        viewsReceived,
     required TResult orElse(),
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
-    required TResult Function(AppsReceived value) appsReceived,
+    required TResult Function(_Initial value) initial,
+    required TResult Function(ViewsReceived value) viewsReceived,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
-    TResult Function(AppsReceived value)? appsReceived,
+    TResult Function(_Initial value)? initial,
+    TResult Function(ViewsReceived value)? viewsReceived,
     required TResult orElse(),
   }) =>
       throw _privateConstructorUsedError;
-
-  @JsonKey(ignore: true)
-  $AppEventCopyWith<AppEvent> get copyWith =>
-      throw _privateConstructorUsedError;
 }
 
 /// @nodoc
 abstract class $AppEventCopyWith<$Res> {
   factory $AppEventCopyWith(AppEvent value, $Res Function(AppEvent) then) =
       _$AppEventCopyWithImpl<$Res>;
-  $Res call({Either<List<App>, WorkspaceError> appsOrFail});
 }
 
 /// @nodoc
@@ -75,69 +75,141 @@ class _$AppEventCopyWithImpl<$Res> implements $AppEventCopyWith<$Res> {
   final AppEvent _value;
   // ignore: unused_field
   final $Res Function(AppEvent) _then;
+}
+
+/// @nodoc
+abstract class _$InitialCopyWith<$Res> {
+  factory _$InitialCopyWith(_Initial value, $Res Function(_Initial) then) =
+      __$InitialCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class __$InitialCopyWithImpl<$Res> extends _$AppEventCopyWithImpl<$Res>
+    implements _$InitialCopyWith<$Res> {
+  __$InitialCopyWithImpl(_Initial _value, $Res Function(_Initial) _then)
+      : super(_value, (v) => _then(v as _Initial));
 
   @override
-  $Res call({
-    Object? appsOrFail = freezed,
+  _Initial get _value => super._value as _Initial;
+}
+
+/// @nodoc
+
+class _$_Initial implements _Initial {
+  const _$_Initial();
+
+  @override
+  String toString() {
+    return 'AppEvent.initial()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is _Initial);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(Either<List<View>, WorkspaceError> appsOrFail)
+        viewsReceived,
   }) {
-    return _then(_value.copyWith(
-      appsOrFail: appsOrFail == freezed
-          ? _value.appsOrFail
-          : appsOrFail // ignore: cast_nullable_to_non_nullable
-              as Either<List<App>, WorkspaceError>,
-    ));
+    return initial();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(Either<List<View>, WorkspaceError> appsOrFail)?
+        viewsReceived,
+    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(ViewsReceived value) viewsReceived,
+  }) {
+    return initial(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(ViewsReceived value)? viewsReceived,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial(this);
+    }
+    return orElse();
   }
 }
 
+abstract class _Initial implements AppEvent {
+  const factory _Initial() = _$_Initial;
+}
+
 /// @nodoc
-abstract class $AppsReceivedCopyWith<$Res> implements $AppEventCopyWith<$Res> {
-  factory $AppsReceivedCopyWith(
-          AppsReceived value, $Res Function(AppsReceived) then) =
-      _$AppsReceivedCopyWithImpl<$Res>;
-  @override
-  $Res call({Either<List<App>, WorkspaceError> appsOrFail});
+abstract class $ViewsReceivedCopyWith<$Res> {
+  factory $ViewsReceivedCopyWith(
+          ViewsReceived value, $Res Function(ViewsReceived) then) =
+      _$ViewsReceivedCopyWithImpl<$Res>;
+  $Res call({Either<List<View>, WorkspaceError> appsOrFail});
 }
 
 /// @nodoc
-class _$AppsReceivedCopyWithImpl<$Res> extends _$AppEventCopyWithImpl<$Res>
-    implements $AppsReceivedCopyWith<$Res> {
-  _$AppsReceivedCopyWithImpl(
-      AppsReceived _value, $Res Function(AppsReceived) _then)
-      : super(_value, (v) => _then(v as AppsReceived));
+class _$ViewsReceivedCopyWithImpl<$Res> extends _$AppEventCopyWithImpl<$Res>
+    implements $ViewsReceivedCopyWith<$Res> {
+  _$ViewsReceivedCopyWithImpl(
+      ViewsReceived _value, $Res Function(ViewsReceived) _then)
+      : super(_value, (v) => _then(v as ViewsReceived));
 
   @override
-  AppsReceived get _value => super._value as AppsReceived;
+  ViewsReceived get _value => super._value as ViewsReceived;
 
   @override
   $Res call({
     Object? appsOrFail = freezed,
   }) {
-    return _then(AppsReceived(
+    return _then(ViewsReceived(
       appsOrFail == freezed
           ? _value.appsOrFail
           : appsOrFail // ignore: cast_nullable_to_non_nullable
-              as Either<List<App>, WorkspaceError>,
+              as Either<List<View>, WorkspaceError>,
     ));
   }
 }
 
 /// @nodoc
 
-class _$AppsReceived implements AppsReceived {
-  const _$AppsReceived(this.appsOrFail);
+class _$ViewsReceived implements ViewsReceived {
+  const _$ViewsReceived(this.appsOrFail);
 
   @override
-  final Either<List<App>, WorkspaceError> appsOrFail;
+  final Either<List<View>, WorkspaceError> appsOrFail;
 
   @override
   String toString() {
-    return 'AppEvent.appsReceived(appsOrFail: $appsOrFail)';
+    return 'AppEvent.viewsReceived(appsOrFail: $appsOrFail)';
   }
 
   @override
   bool operator ==(dynamic other) {
     return identical(this, other) ||
-        (other is AppsReceived &&
+        (other is ViewsReceived &&
             (identical(other.appsOrFail, appsOrFail) ||
                 const DeepCollectionEquality()
                     .equals(other.appsOrFail, appsOrFail)));
@@ -149,27 +221,29 @@ class _$AppsReceived implements AppsReceived {
 
   @JsonKey(ignore: true)
   @override
-  $AppsReceivedCopyWith<AppsReceived> get copyWith =>
-      _$AppsReceivedCopyWithImpl<AppsReceived>(this, _$identity);
+  $ViewsReceivedCopyWith<ViewsReceived> get copyWith =>
+      _$ViewsReceivedCopyWithImpl<ViewsReceived>(this, _$identity);
 
   @override
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
-    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
-        appsReceived,
+    required TResult Function() initial,
+    required TResult Function(Either<List<View>, WorkspaceError> appsOrFail)
+        viewsReceived,
   }) {
-    return appsReceived(appsOrFail);
+    return viewsReceived(appsOrFail);
   }
 
   @override
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
-    TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
-        appsReceived,
+    TResult Function()? initial,
+    TResult Function(Either<List<View>, WorkspaceError> appsOrFail)?
+        viewsReceived,
     required TResult orElse(),
   }) {
-    if (appsReceived != null) {
-      return appsReceived(appsOrFail);
+    if (viewsReceived != null) {
+      return viewsReceived(appsOrFail);
     }
     return orElse();
   }
@@ -177,34 +251,34 @@ class _$AppsReceived implements AppsReceived {
   @override
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
-    required TResult Function(AppsReceived value) appsReceived,
+    required TResult Function(_Initial value) initial,
+    required TResult Function(ViewsReceived value) viewsReceived,
   }) {
-    return appsReceived(this);
+    return viewsReceived(this);
   }
 
   @override
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
-    TResult Function(AppsReceived value)? appsReceived,
+    TResult Function(_Initial value)? initial,
+    TResult Function(ViewsReceived value)? viewsReceived,
     required TResult orElse(),
   }) {
-    if (appsReceived != null) {
-      return appsReceived(this);
+    if (viewsReceived != null) {
+      return viewsReceived(this);
     }
     return orElse();
   }
 }
 
-abstract class AppsReceived implements AppEvent {
-  const factory AppsReceived(Either<List<App>, WorkspaceError> appsOrFail) =
-      _$AppsReceived;
+abstract class ViewsReceived implements AppEvent {
+  const factory ViewsReceived(Either<List<View>, WorkspaceError> appsOrFail) =
+      _$ViewsReceived;
 
-  @override
-  Either<List<App>, WorkspaceError> get appsOrFail =>
+  Either<List<View>, WorkspaceError> get appsOrFail =>
       throw _privateConstructorUsedError;
-  @override
   @JsonKey(ignore: true)
-  $AppsReceivedCopyWith<AppsReceived> get copyWith =>
+  $ViewsReceivedCopyWith<ViewsReceived> get copyWith =>
       throw _privateConstructorUsedError;
 }
 
@@ -213,9 +287,11 @@ class _$AppStateTearOff {
   const _$AppStateTearOff();
 
   _AppState call(
-      {required Option<List<App>> apps,
+      {required bool isLoading,
+      required Option<List<App>> apps,
       required Either<Unit, WorkspaceError> successOrFailure}) {
     return _AppState(
+      isLoading: isLoading,
       apps: apps,
       successOrFailure: successOrFailure,
     );
@@ -227,6 +303,7 @@ const $AppState = _$AppStateTearOff();
 
 /// @nodoc
 mixin _$AppState {
+  bool get isLoading => throw _privateConstructorUsedError;
   Option<List<App>> get apps => throw _privateConstructorUsedError;
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
@@ -241,7 +318,9 @@ abstract class $AppStateCopyWith<$Res> {
   factory $AppStateCopyWith(AppState value, $Res Function(AppState) then) =
       _$AppStateCopyWithImpl<$Res>;
   $Res call(
-      {Option<List<App>> apps, Either<Unit, WorkspaceError> successOrFailure});
+      {bool isLoading,
+      Option<List<App>> apps,
+      Either<Unit, WorkspaceError> successOrFailure});
 }
 
 /// @nodoc
@@ -254,10 +333,15 @@ class _$AppStateCopyWithImpl<$Res> implements $AppStateCopyWith<$Res> {
 
   @override
   $Res call({
+    Object? isLoading = freezed,
     Object? apps = freezed,
     Object? successOrFailure = freezed,
   }) {
     return _then(_value.copyWith(
+      isLoading: isLoading == freezed
+          ? _value.isLoading
+          : isLoading // ignore: cast_nullable_to_non_nullable
+              as bool,
       apps: apps == freezed
           ? _value.apps
           : apps // ignore: cast_nullable_to_non_nullable
@@ -276,7 +360,9 @@ abstract class _$AppStateCopyWith<$Res> implements $AppStateCopyWith<$Res> {
       __$AppStateCopyWithImpl<$Res>;
   @override
   $Res call(
-      {Option<List<App>> apps, Either<Unit, WorkspaceError> successOrFailure});
+      {bool isLoading,
+      Option<List<App>> apps,
+      Either<Unit, WorkspaceError> successOrFailure});
 }
 
 /// @nodoc
@@ -290,10 +376,15 @@ class __$AppStateCopyWithImpl<$Res> extends _$AppStateCopyWithImpl<$Res>
 
   @override
   $Res call({
+    Object? isLoading = freezed,
     Object? apps = freezed,
     Object? successOrFailure = freezed,
   }) {
     return _then(_AppState(
+      isLoading: isLoading == freezed
+          ? _value.isLoading
+          : isLoading // ignore: cast_nullable_to_non_nullable
+              as bool,
       apps: apps == freezed
           ? _value.apps
           : apps // ignore: cast_nullable_to_non_nullable
@@ -309,8 +400,13 @@ class __$AppStateCopyWithImpl<$Res> extends _$AppStateCopyWithImpl<$Res>
 /// @nodoc
 
 class _$_AppState implements _AppState {
-  const _$_AppState({required this.apps, required this.successOrFailure});
+  const _$_AppState(
+      {required this.isLoading,
+      required this.apps,
+      required this.successOrFailure});
 
+  @override
+  final bool isLoading;
   @override
   final Option<List<App>> apps;
   @override
@@ -318,13 +414,16 @@ class _$_AppState implements _AppState {
 
   @override
   String toString() {
-    return 'AppState(apps: $apps, successOrFailure: $successOrFailure)';
+    return 'AppState(isLoading: $isLoading, apps: $apps, successOrFailure: $successOrFailure)';
   }
 
   @override
   bool operator ==(dynamic other) {
     return identical(this, other) ||
         (other is _AppState &&
+            (identical(other.isLoading, isLoading) ||
+                const DeepCollectionEquality()
+                    .equals(other.isLoading, isLoading)) &&
             (identical(other.apps, apps) ||
                 const DeepCollectionEquality().equals(other.apps, apps)) &&
             (identical(other.successOrFailure, successOrFailure) ||
@@ -335,6 +434,7 @@ class _$_AppState implements _AppState {
   @override
   int get hashCode =>
       runtimeType.hashCode ^
+      const DeepCollectionEquality().hash(isLoading) ^
       const DeepCollectionEquality().hash(apps) ^
       const DeepCollectionEquality().hash(successOrFailure);
 
@@ -346,9 +446,12 @@ class _$_AppState implements _AppState {
 
 abstract class _AppState implements AppState {
   const factory _AppState(
-      {required Option<List<App>> apps,
+      {required bool isLoading,
+      required Option<List<App>> apps,
       required Either<Unit, WorkspaceError> successOrFailure}) = _$_AppState;
 
+  @override
+  bool get isLoading => throw _privateConstructorUsedError;
   @override
   Option<List<App>> get apps => throw _privateConstructorUsedError;
   @override

+ 3 - 2
app_flowy/lib/home/application/app/app_event.dart

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

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

@@ -3,11 +3,13 @@ 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),
       );

+ 19 - 4
app_flowy/lib/home/application/menu/menu_bloc.dart

@@ -2,6 +2,7 @@ import 'dart:async';
 import 'package:app_flowy/home/domain/i_workspace.dart';
 import 'package:app_flowy/home/domain/page_context.dart';
 import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
@@ -19,24 +20,38 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
     MenuEvent event,
   ) async* {
     yield* event.map(
+      initial: (value) async* {
+        iWorkspaceImpl.startWatching(addAppCallback: (appsOrFail) {
+          appsOrFail.fold(
+            (apps) => add(MenuEvent.appsReceived(left(apps))),
+            (error) => add(MenuEvent.appsReceived(right(error))),
+          );
+        });
+      },
       collapse: (e) async* {
         final isCollapse = state.isCollapse;
         yield state.copyWith(isCollapse: !isCollapse);
       },
-      openPage: (e) async* {
+      openPage: (OpenPage e) async* {
         yield* _performActionOnOpenPage(e);
       },
-      createApp: (event) async* {
+      createApp: (CreateApp event) async* {
         yield* _performActionOnCreateApp(event);
       },
+      appsReceived: (AppsReceived value) async* {
+        yield value.appsOrFail.fold(
+          (apps) => state.copyWith(apps: some(apps)),
+          (error) => state.copyWith(successOrFailure: right(error)),
+        );
+      },
     );
   }
 
-  Stream<MenuState> _performActionOnOpenPage(_OpenPage e) async* {
+  Stream<MenuState> _performActionOnOpenPage(OpenPage e) async* {
     yield state.copyWith(pageContext: some(e.context));
   }
 
-  Stream<MenuState> _performActionOnCreateApp(_CreateApp event) async* {
+  Stream<MenuState> _performActionOnCreateApp(CreateApp event) async* {
     await iWorkspaceImpl
         .createApp(name: event.name, desc: event.desc)
         .then((result) async* {

+ 358 - 56
app_flowy/lib/home/application/menu/menu_bloc.freezed.dart

@@ -16,22 +16,32 @@ final _privateConstructorUsedError = UnsupportedError(
 class _$MenuEventTearOff {
   const _$MenuEventTearOff();
 
+  _Initial initial() {
+    return const _Initial();
+  }
+
   Collapse collapse() {
     return const Collapse();
   }
 
-  _OpenPage openPage(PageContext context) {
-    return _OpenPage(
+  OpenPage openPage(PageContext context) {
+    return OpenPage(
       context,
     );
   }
 
-  _CreateApp createApp(String name, {String? desc}) {
-    return _CreateApp(
+  CreateApp createApp(String name, {String? desc}) {
+    return CreateApp(
       name,
       desc: desc,
     );
   }
+
+  AppsReceived appsReceived(Either<List<App>, WorkspaceError> appsOrFail) {
+    return AppsReceived(
+      appsOrFail,
+    );
+  }
 }
 
 /// @nodoc
@@ -41,31 +51,41 @@ const $MenuEvent = _$MenuEventTearOff();
 mixin _$MenuEvent {
   @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,
   }) =>
       throw _privateConstructorUsedError;
   @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(),
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
     required TResult Function(Collapse value) collapse,
-    required TResult Function(_OpenPage value) openPage,
-    required TResult Function(_CreateApp value) createApp,
+    required TResult Function(OpenPage value) openPage,
+    required TResult Function(CreateApp value) createApp,
+    required TResult Function(AppsReceived value) appsReceived,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
     TResult Function(Collapse value)? collapse,
-    TResult Function(_OpenPage value)? openPage,
-    TResult Function(_CreateApp value)? createApp,
+    TResult Function(OpenPage value)? openPage,
+    TResult Function(CreateApp value)? createApp,
+    TResult Function(AppsReceived value)? appsReceived,
     required TResult orElse(),
   }) =>
       throw _privateConstructorUsedError;
@@ -86,6 +106,103 @@ class _$MenuEventCopyWithImpl<$Res> implements $MenuEventCopyWith<$Res> {
   final $Res Function(MenuEvent) _then;
 }
 
+/// @nodoc
+abstract class _$InitialCopyWith<$Res> {
+  factory _$InitialCopyWith(_Initial value, $Res Function(_Initial) then) =
+      __$InitialCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class __$InitialCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
+    implements _$InitialCopyWith<$Res> {
+  __$InitialCopyWithImpl(_Initial _value, $Res Function(_Initial) _then)
+      : super(_value, (v) => _then(v as _Initial));
+
+  @override
+  _Initial get _value => super._value as _Initial;
+}
+
+/// @nodoc
+
+class _$_Initial implements _Initial {
+  const _$_Initial();
+
+  @override
+  String toString() {
+    return 'MenuEvent.initial()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is _Initial);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function() collapse,
+    required TResult Function(PageContext context) openPage,
+    required TResult Function(String name, String? desc) createApp,
+    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
+        appsReceived,
+  }) {
+    return initial();
+  }
+
+  @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 (initial != null) {
+      return initial();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(_Initial value) initial,
+    required TResult Function(Collapse value) collapse,
+    required TResult Function(OpenPage value) openPage,
+    required TResult Function(CreateApp value) createApp,
+    required TResult Function(AppsReceived value) appsReceived,
+  }) {
+    return initial(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(_Initial value)? initial,
+    TResult Function(Collapse value)? collapse,
+    TResult Function(OpenPage value)? openPage,
+    TResult Function(CreateApp value)? createApp,
+    TResult Function(AppsReceived value)? appsReceived,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class _Initial implements MenuEvent {
+  const factory _Initial() = _$_Initial;
+}
+
 /// @nodoc
 abstract class $CollapseCopyWith<$Res> {
   factory $CollapseCopyWith(Collapse value, $Res Function(Collapse) then) =
@@ -123,9 +240,12 @@ class _$Collapse implements Collapse {
   @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 collapse();
   }
@@ -133,9 +253,12 @@ class _$Collapse implements Collapse {
   @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 (collapse != null) {
@@ -147,9 +270,11 @@ class _$Collapse implements Collapse {
   @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(OpenPage value) openPage,
+    required TResult Function(CreateApp value) createApp,
+    required TResult Function(AppsReceived value) appsReceived,
   }) {
     return collapse(this);
   }
@@ -157,9 +282,11 @@ class _$Collapse implements Collapse {
   @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(OpenPage value)? openPage,
+    TResult Function(CreateApp value)? createApp,
+    TResult Function(AppsReceived value)? appsReceived,
     required TResult orElse(),
   }) {
     if (collapse != null) {
@@ -174,26 +301,26 @@ abstract class Collapse implements MenuEvent {
 }
 
 /// @nodoc
-abstract class _$OpenPageCopyWith<$Res> {
-  factory _$OpenPageCopyWith(_OpenPage value, $Res Function(_OpenPage) then) =
-      __$OpenPageCopyWithImpl<$Res>;
+abstract class $OpenPageCopyWith<$Res> {
+  factory $OpenPageCopyWith(OpenPage value, $Res Function(OpenPage) then) =
+      _$OpenPageCopyWithImpl<$Res>;
   $Res call({PageContext context});
 }
 
 /// @nodoc
-class __$OpenPageCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
-    implements _$OpenPageCopyWith<$Res> {
-  __$OpenPageCopyWithImpl(_OpenPage _value, $Res Function(_OpenPage) _then)
-      : super(_value, (v) => _then(v as _OpenPage));
+class _$OpenPageCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
+    implements $OpenPageCopyWith<$Res> {
+  _$OpenPageCopyWithImpl(OpenPage _value, $Res Function(OpenPage) _then)
+      : super(_value, (v) => _then(v as OpenPage));
 
   @override
-  _OpenPage get _value => super._value as _OpenPage;
+  OpenPage get _value => super._value as OpenPage;
 
   @override
   $Res call({
     Object? context = freezed,
   }) {
-    return _then(_OpenPage(
+    return _then(OpenPage(
       context == freezed
           ? _value.context
           : context // ignore: cast_nullable_to_non_nullable
@@ -204,8 +331,8 @@ class __$OpenPageCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
 
 /// @nodoc
 
-class _$_OpenPage implements _OpenPage {
-  const _$_OpenPage(this.context);
+class _$OpenPage implements OpenPage {
+  const _$OpenPage(this.context);
 
   @override
   final PageContext context;
@@ -218,7 +345,7 @@ class _$_OpenPage implements _OpenPage {
   @override
   bool operator ==(dynamic other) {
     return identical(this, other) ||
-        (other is _OpenPage &&
+        (other is OpenPage &&
             (identical(other.context, context) ||
                 const DeepCollectionEquality().equals(other.context, context)));
   }
@@ -229,15 +356,18 @@ class _$_OpenPage implements _OpenPage {
 
   @JsonKey(ignore: true)
   @override
-  _$OpenPageCopyWith<_OpenPage> get copyWith =>
-      __$OpenPageCopyWithImpl<_OpenPage>(this, _$identity);
+  $OpenPageCopyWith<OpenPage> get copyWith =>
+      _$OpenPageCopyWithImpl<OpenPage>(this, _$identity);
 
   @override
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
+    required TResult Function() initial,
     required TResult Function() collapse,
     required TResult Function(PageContext context) openPage,
     required TResult Function(String name, String? desc) createApp,
+    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
+        appsReceived,
   }) {
     return openPage(context);
   }
@@ -245,9 +375,12 @@ class _$_OpenPage implements _OpenPage {
   @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 (openPage != null) {
@@ -259,9 +392,11 @@ class _$_OpenPage implements _OpenPage {
   @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(OpenPage value) openPage,
+    required TResult Function(CreateApp value) createApp,
+    required TResult Function(AppsReceived value) appsReceived,
   }) {
     return openPage(this);
   }
@@ -269,9 +404,11 @@ class _$_OpenPage implements _OpenPage {
   @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(OpenPage value)? openPage,
+    TResult Function(CreateApp value)? createApp,
+    TResult Function(AppsReceived value)? appsReceived,
     required TResult orElse(),
   }) {
     if (openPage != null) {
@@ -281,38 +418,37 @@ class _$_OpenPage implements _OpenPage {
   }
 }
 
-abstract class _OpenPage implements MenuEvent {
-  const factory _OpenPage(PageContext context) = _$_OpenPage;
+abstract class OpenPage implements MenuEvent {
+  const factory OpenPage(PageContext context) = _$OpenPage;
 
   PageContext get context => throw _privateConstructorUsedError;
   @JsonKey(ignore: true)
-  _$OpenPageCopyWith<_OpenPage> get copyWith =>
+  $OpenPageCopyWith<OpenPage> get copyWith =>
       throw _privateConstructorUsedError;
 }
 
 /// @nodoc
-abstract class _$CreateAppCopyWith<$Res> {
-  factory _$CreateAppCopyWith(
-          _CreateApp value, $Res Function(_CreateApp) then) =
-      __$CreateAppCopyWithImpl<$Res>;
+abstract class $CreateAppCopyWith<$Res> {
+  factory $CreateAppCopyWith(CreateApp value, $Res Function(CreateApp) then) =
+      _$CreateAppCopyWithImpl<$Res>;
   $Res call({String name, String? desc});
 }
 
 /// @nodoc
-class __$CreateAppCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
-    implements _$CreateAppCopyWith<$Res> {
-  __$CreateAppCopyWithImpl(_CreateApp _value, $Res Function(_CreateApp) _then)
-      : super(_value, (v) => _then(v as _CreateApp));
+class _$CreateAppCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
+    implements $CreateAppCopyWith<$Res> {
+  _$CreateAppCopyWithImpl(CreateApp _value, $Res Function(CreateApp) _then)
+      : super(_value, (v) => _then(v as CreateApp));
 
   @override
-  _CreateApp get _value => super._value as _CreateApp;
+  CreateApp get _value => super._value as CreateApp;
 
   @override
   $Res call({
     Object? name = freezed,
     Object? desc = freezed,
   }) {
-    return _then(_CreateApp(
+    return _then(CreateApp(
       name == freezed
           ? _value.name
           : name // ignore: cast_nullable_to_non_nullable
@@ -327,8 +463,8 @@ class __$CreateAppCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
 
 /// @nodoc
 
-class _$_CreateApp implements _CreateApp {
-  const _$_CreateApp(this.name, {this.desc});
+class _$CreateApp implements CreateApp {
+  const _$CreateApp(this.name, {this.desc});
 
   @override
   final String name;
@@ -343,7 +479,7 @@ class _$_CreateApp implements _CreateApp {
   @override
   bool operator ==(dynamic other) {
     return identical(this, other) ||
-        (other is _CreateApp &&
+        (other is CreateApp &&
             (identical(other.name, name) ||
                 const DeepCollectionEquality().equals(other.name, name)) &&
             (identical(other.desc, desc) ||
@@ -358,15 +494,18 @@ class _$_CreateApp implements _CreateApp {
 
   @JsonKey(ignore: true)
   @override
-  _$CreateAppCopyWith<_CreateApp> get copyWith =>
-      __$CreateAppCopyWithImpl<_CreateApp>(this, _$identity);
+  $CreateAppCopyWith<CreateApp> get copyWith =>
+      _$CreateAppCopyWithImpl<CreateApp>(this, _$identity);
 
   @override
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
+    required TResult Function() initial,
     required TResult Function() collapse,
     required TResult Function(PageContext context) openPage,
     required TResult Function(String name, String? desc) createApp,
+    required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
+        appsReceived,
   }) {
     return createApp(name, desc);
   }
@@ -374,9 +513,12 @@ class _$_CreateApp implements _CreateApp {
   @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 (createApp != null) {
@@ -388,9 +530,11 @@ class _$_CreateApp implements _CreateApp {
   @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(OpenPage value) openPage,
+    required TResult Function(CreateApp value) createApp,
+    required TResult Function(AppsReceived value) appsReceived,
   }) {
     return createApp(this);
   }
@@ -398,9 +542,11 @@ class _$_CreateApp implements _CreateApp {
   @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(OpenPage value)? openPage,
+    TResult Function(CreateApp value)? createApp,
+    TResult Function(AppsReceived value)? appsReceived,
     required TResult orElse(),
   }) {
     if (createApp != null) {
@@ -410,13 +556,145 @@ class _$_CreateApp implements _CreateApp {
   }
 }
 
-abstract class _CreateApp implements MenuEvent {
-  const factory _CreateApp(String name, {String? desc}) = _$_CreateApp;
+abstract class CreateApp implements MenuEvent {
+  const factory CreateApp(String name, {String? desc}) = _$CreateApp;
 
   String get name => throw _privateConstructorUsedError;
   String? get desc => throw _privateConstructorUsedError;
   @JsonKey(ignore: true)
-  _$CreateAppCopyWith<_CreateApp> get copyWith =>
+  $CreateAppCopyWith<CreateApp> get copyWith =>
+      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;
 }
 
@@ -427,10 +705,12 @@ class _$MenuStateTearOff {
   _MenuState call(
       {required bool isCollapse,
       required Option<PageContext> pageContext,
+      required Option<List<App>> apps,
       required Either<Unit, WorkspaceError> successOrFailure}) {
     return _MenuState(
       isCollapse: isCollapse,
       pageContext: pageContext,
+      apps: apps,
       successOrFailure: successOrFailure,
     );
   }
@@ -443,6 +723,7 @@ const $MenuState = _$MenuStateTearOff();
 mixin _$MenuState {
   bool get isCollapse => throw _privateConstructorUsedError;
   Option<PageContext> get pageContext => throw _privateConstructorUsedError;
+  Option<List<App>> get apps => throw _privateConstructorUsedError;
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
 
@@ -458,6 +739,7 @@ abstract class $MenuStateCopyWith<$Res> {
   $Res call(
       {bool isCollapse,
       Option<PageContext> pageContext,
+      Option<List<App>> apps,
       Either<Unit, WorkspaceError> successOrFailure});
 }
 
@@ -473,6 +755,7 @@ class _$MenuStateCopyWithImpl<$Res> implements $MenuStateCopyWith<$Res> {
   $Res call({
     Object? isCollapse = freezed,
     Object? pageContext = freezed,
+    Object? apps = freezed,
     Object? successOrFailure = freezed,
   }) {
     return _then(_value.copyWith(
@@ -484,6 +767,10 @@ class _$MenuStateCopyWithImpl<$Res> implements $MenuStateCopyWith<$Res> {
           ? _value.pageContext
           : pageContext // ignore: cast_nullable_to_non_nullable
               as Option<PageContext>,
+      apps: apps == freezed
+          ? _value.apps
+          : apps // ignore: cast_nullable_to_non_nullable
+              as Option<List<App>>,
       successOrFailure: successOrFailure == freezed
           ? _value.successOrFailure
           : successOrFailure // ignore: cast_nullable_to_non_nullable
@@ -501,6 +788,7 @@ abstract class _$MenuStateCopyWith<$Res> implements $MenuStateCopyWith<$Res> {
   $Res call(
       {bool isCollapse,
       Option<PageContext> pageContext,
+      Option<List<App>> apps,
       Either<Unit, WorkspaceError> successOrFailure});
 }
 
@@ -517,6 +805,7 @@ class __$MenuStateCopyWithImpl<$Res> extends _$MenuStateCopyWithImpl<$Res>
   $Res call({
     Object? isCollapse = freezed,
     Object? pageContext = freezed,
+    Object? apps = freezed,
     Object? successOrFailure = freezed,
   }) {
     return _then(_MenuState(
@@ -528,6 +817,10 @@ class __$MenuStateCopyWithImpl<$Res> extends _$MenuStateCopyWithImpl<$Res>
           ? _value.pageContext
           : pageContext // ignore: cast_nullable_to_non_nullable
               as Option<PageContext>,
+      apps: apps == freezed
+          ? _value.apps
+          : apps // ignore: cast_nullable_to_non_nullable
+              as Option<List<App>>,
       successOrFailure: successOrFailure == freezed
           ? _value.successOrFailure
           : successOrFailure // ignore: cast_nullable_to_non_nullable
@@ -542,6 +835,7 @@ class _$_MenuState implements _MenuState {
   const _$_MenuState(
       {required this.isCollapse,
       required this.pageContext,
+      required this.apps,
       required this.successOrFailure});
 
   @override
@@ -549,11 +843,13 @@ class _$_MenuState implements _MenuState {
   @override
   final Option<PageContext> pageContext;
   @override
+  final Option<List<App>> apps;
+  @override
   final Either<Unit, WorkspaceError> successOrFailure;
 
   @override
   String toString() {
-    return 'MenuState(isCollapse: $isCollapse, pageContext: $pageContext, successOrFailure: $successOrFailure)';
+    return 'MenuState(isCollapse: $isCollapse, pageContext: $pageContext, apps: $apps, successOrFailure: $successOrFailure)';
   }
 
   @override
@@ -566,6 +862,8 @@ class _$_MenuState implements _MenuState {
             (identical(other.pageContext, pageContext) ||
                 const DeepCollectionEquality()
                     .equals(other.pageContext, pageContext)) &&
+            (identical(other.apps, apps) ||
+                const DeepCollectionEquality().equals(other.apps, apps)) &&
             (identical(other.successOrFailure, successOrFailure) ||
                 const DeepCollectionEquality()
                     .equals(other.successOrFailure, successOrFailure)));
@@ -576,6 +874,7 @@ class _$_MenuState implements _MenuState {
       runtimeType.hashCode ^
       const DeepCollectionEquality().hash(isCollapse) ^
       const DeepCollectionEquality().hash(pageContext) ^
+      const DeepCollectionEquality().hash(apps) ^
       const DeepCollectionEquality().hash(successOrFailure);
 
   @JsonKey(ignore: true)
@@ -588,6 +887,7 @@ 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;
 
   @override
@@ -595,6 +895,8 @@ abstract class _MenuState implements MenuState {
   @override
   Option<PageContext> get pageContext => throw _privateConstructorUsedError;
   @override
+  Option<List<App>> get apps => throw _privateConstructorUsedError;
+  @override
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
   @override

+ 5 - 2
app_flowy/lib/home/application/menu/menu_event.dart

@@ -2,7 +2,10 @@ 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.openPage(PageContext context) = OpenPage;
+  const factory MenuEvent.createApp(String name, {String? desc}) = CreateApp;
+  const factory MenuEvent.appsReceived(
+      Either<List<App>, WorkspaceError> appsOrFail) = AppsReceived;
 }

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

@@ -5,12 +5,14 @@ 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),
       );
 }

+ 11 - 1
app_flowy/lib/home/domain/i_app.dart

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

+ 10 - 0
app_flowy/lib/home/domain/i_workspace.dart

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

+ 7 - 4
app_flowy/lib/home/infrastructure/deps_resolver.dart

@@ -14,11 +14,14 @@ class HomeDepsResolver {
         (appId, _) => AppRepository(appId: appId));
 
     //Interface implementation
-    getIt.registerFactory<IApp>(() => IAppImpl(repo: getIt<AppRepository>()));
-    getIt.registerFactory<IWorkspace>(
-        () => IWorkspaceImpl(repo: getIt<WorkspaceRepository>()));
+    getIt.registerFactoryParam<IApp, String, void>(
+        (appId, _) => IAppImpl(repo: getIt<AppRepository>(param1: appId)));
+
+    getIt.registerFactoryParam<IWorkspace, String, void>((workspacId, _) =>
+        IWorkspaceImpl(repo: getIt<WorkspaceRepository>(param1: workspacId)));
 
     //Bloc
-    getIt.registerFactory<MenuBloc>(() => MenuBloc(getIt<IWorkspace>()));
+    getIt.registerFactoryParam<MenuBloc, String, void>(
+        (workspaceId, _) => MenuBloc(getIt<IWorkspace>(param1: workspaceId)));
   }
 }

+ 14 - 1
app_flowy/lib/home/infrastructure/i_app_impl.dart

@@ -22,7 +22,20 @@ class IAppImpl extends IApp {
       {required String appId,
       required String name,
       String? desc,
-      required ViewTypeIdentifier viewType}) {
+      required ViewType viewType}) {
     return repo.createView(appId, name, desc ?? "", viewType);
   }
+
+  @override
+  void startWatching(
+      {AppAddViewCallback? addViewCallback,
+      AppUpdatedCallback? updatedCallback}) {
+    repo.startWatching(
+        addViewCallback: addViewCallback, updatedCallback: updatedCallback);
+  }
+
+  @override
+  Future<void> stopWatching() async {
+    await repo.close();
+  }
 }

+ 21 - 1
app_flowy/lib/home/infrastructure/i_workspace_impl.dart

@@ -21,6 +21,26 @@ class IWorkspaceImpl extends IWorkspace {
   @override
   Future<Either<List<App>, WorkspaceError>> getApps(
       {required String workspaceId}) {
-    return repo.getApps(workspaceId: workspaceId);
+    return repo
+        .getWorkspace(workspaceId: workspaceId, readApps: true)
+        .then((result) {
+      return result.fold(
+        (workspace) => left(workspace.apps.items),
+        (error) => right(error),
+      );
+    });
+  }
+
+  @override
+  void startWatching(
+      {WorkspaceAddAppCallback? addAppCallback,
+      WorkspaceUpdatedCallback? updatedCallback}) {
+    repo.startWatching(
+        addAppCallback: addAppCallback, updatedCallback: updatedCallback);
+  }
+
+  @override
+  Future<void> stopWatching() async {
+    await repo.close();
   }
 }

+ 17 - 19
app_flowy/lib/home/infrastructure/repos/app_repo.dart

@@ -1,4 +1,5 @@
 import 'dart:async';
+import 'package:app_flowy/home/domain/i_app.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_infra/flowy_logger.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
@@ -11,13 +12,10 @@ import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pbenum.dart';
 import 'package:flowy_sdk/rust_stream.dart';
 
-typedef AppUpdatedCallback = void Function(String name, String desc);
-typedef ViewUpdatedCallback = void Function(List<View> views);
-
 class AppRepository {
   StreamSubscription<ObservableSubject>? _subscription;
-  ViewUpdatedCallback? _viewUpdatedCallback;
-  AppUpdatedCallback? _appUpdatedCallback;
+  AppAddViewCallback? _addViewCallback;
+  AppUpdatedCallback? _updatedCallback;
   String appId;
   AppRepository({
     required this.appId,
@@ -32,7 +30,7 @@ class AppRepository {
   }
 
   Future<Either<View, WorkspaceError>> createView(
-      String appId, String name, String desc, ViewTypeIdentifier viewType) {
+      String appId, String name, String desc, ViewType viewType) {
     final request = CreateViewRequest.create()
       ..appId = appId
       ..name = name
@@ -56,42 +54,42 @@ class AppRepository {
   }
 
   void startWatching(
-      {ViewUpdatedCallback? viewUpdatedCallback,
-      AppUpdatedCallback? appUpdatedCallback}) {
-    _viewUpdatedCallback = viewUpdatedCallback;
-    _appUpdatedCallback = appUpdatedCallback;
+      {AppAddViewCallback? addViewCallback,
+      AppUpdatedCallback? updatedCallback}) {
+    _addViewCallback = addViewCallback;
+    _updatedCallback = updatedCallback;
     _subscription = RustStreamReceiver.listen((observable) {
       if (observable.subjectId != appId) {
         return;
       }
 
-      final ty = WorkspaceObservableType.valueOf(observable.ty);
+      final ty = WorkspaceObservable.valueOf(observable.ty);
       if (ty != null) {
         _handleObservableType(ty);
       }
     });
   }
 
-  void _handleObservableType(WorkspaceObservableType ty) {
+  void _handleObservableType(WorkspaceObservable ty) {
     switch (ty) {
-      case WorkspaceObservableType.ViewUpdated:
-        if (_viewUpdatedCallback == null) {
+      case WorkspaceObservable.AppAddView:
+        if (_addViewCallback == null) {
           return;
         }
         getViews(appId: appId).then((result) {
           result.fold(
-            (views) => _viewUpdatedCallback!(views),
-            (error) => Log.error(error),
+            (views) => _addViewCallback!(left(views)),
+            (error) => _addViewCallback!(right(error)),
           );
         });
         break;
-      case WorkspaceObservableType.AppDescUpdated:
-        if (_appUpdatedCallback == null) {
+      case WorkspaceObservable.AppUpdateDesc:
+        if (_updatedCallback == null) {
           return;
         }
         getAppDesc().then((result) {
           result.fold(
-            (app) => _appUpdatedCallback!(app.name, app.desc),
+            (app) => _updatedCallback!(app.name, app.desc),
             (error) => Log.error(error),
           );
         });

+ 31 - 15
app_flowy/lib/home/infrastructure/repos/workspace_repo.dart

@@ -1,5 +1,6 @@
 import 'dart:async';
 
+import 'package:app_flowy/home/domain/i_workspace.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_infra/flowy_logger.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
@@ -7,14 +8,14 @@ import 'package:flowy_sdk/protobuf/flowy-observable/subject.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/observable.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_query.pb.dart';
 import 'package:flowy_sdk/rust_stream.dart';
 
-typedef AppUpdatedCallback = void Function(List<App> apps);
-
 class WorkspaceRepository {
   StreamSubscription<ObservableSubject>? _subscription;
-  AppUpdatedCallback? _appUpdatedCallback;
+  WorkspaceAddAppCallback? _addAppCallback;
+  WorkspaceUpdatedCallback? _updatedCallback;
   String workspaceId;
   WorkspaceRepository({
     required this.workspaceId,
@@ -37,47 +38,62 @@ class WorkspaceRepository {
     });
   }
 
-  Future<Either<List<App>, WorkspaceError>> getApps(
-      {required String workspaceId}) {
+  Future<Either<Workspace, WorkspaceError>> getWorkspace(
+      {required String workspaceId, bool readApps = false}) {
     final request = QueryWorkspaceRequest.create()
       ..workspaceId = workspaceId
-      ..readApps = true;
+      ..readApps = readApps;
 
     return WorkspaceEventGetWorkspace(request).send().then((result) {
       return result.fold(
-        (workspace) => left(workspace.apps.items),
+        (workspace) => left(workspace),
         (error) => right(error),
       );
     });
   }
 
-  void startWatching({AppUpdatedCallback? appUpdatedCallback}) {
-    _appUpdatedCallback = appUpdatedCallback;
+  void startWatching(
+      {WorkspaceAddAppCallback? addAppCallback,
+      WorkspaceUpdatedCallback? updatedCallback}) {
+    _addAppCallback = addAppCallback;
+    _updatedCallback = updatedCallback;
+
     _subscription = RustStreamReceiver.listen((observable) {
       if (observable.subjectId != workspaceId) {
         return;
       }
 
-      final ty = WorkspaceObservableType.valueOf(observable.ty);
+      final ty = WorkspaceObservable.valueOf(observable.ty);
       if (ty != null) {
         _handleObservableType(ty);
       }
     });
   }
 
-  void _handleObservableType(WorkspaceObservableType ty) {
+  void _handleObservableType(WorkspaceObservable ty) {
     switch (ty) {
-      case WorkspaceObservableType.WorkspaceUpdated:
-        if (_appUpdatedCallback == null) {
+      case WorkspaceObservable.WorkspaceUpdateDesc:
+        if (_updatedCallback == null) {
           return;
         }
-        getApps(workspaceId: workspaceId).then((result) {
+        getWorkspace(workspaceId: workspaceId).then((result) {
           result.fold(
-            (apps) => _appUpdatedCallback!(apps),
+            (workspace) => _updatedCallback!(workspace.name, workspace.desc),
             (error) => Log.error(error),
           );
         });
         break;
+      case WorkspaceObservable.WorkspaceAddApp:
+        if (_addAppCallback == null) {
+          return;
+        }
+        getWorkspace(workspaceId: workspaceId, readApps: true).then((result) {
+          result.fold(
+            (workspace) => _addAppCallback!(left(workspace.apps.items)),
+            (error) => _addAppCallback!(right(error)),
+          );
+        });
+        break;
       default:
         break;
     }

+ 1 - 0
app_flowy/lib/home/presentation/home_screen.dart

@@ -80,6 +80,7 @@ class HomeScreen extends StatelessWidget {
       isCollapseChanged: (isCollapse) {
         homeBloc.add(HomeEvent.forceCollapse(isCollapse));
       },
+      workspaceId: userDetail.workspace,
     );
     homeMenu = RepaintBoundary(child: homeMenu);
     homeMenu = FocusTraversalGroup(child: homeMenu);

+ 58 - 0
app_flowy/lib/home/presentation/widgets/app/app_list_widget.dart

@@ -0,0 +1,58 @@
+import 'package:app_flowy/home/application/app/app_bloc.dart';
+import 'package:app_flowy/startup/startup.dart';
+// ignore: import_of_legacy_library_into_null_safe
+import 'package:expandable/expandable.dart';
+import 'package:flowy_infra/flowy_logger.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flowy_infra_ui/widget/error_page.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),
+          );
+        },
+      ),
+    );
+  }
+
+  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(),
+          ),
+        ));
+  }
+}

+ 122 - 0
app_flowy/lib/home/presentation/widgets/app/app_widget.dart

@@ -0,0 +1,122 @@
+// ignore: import_of_legacy_library_into_null_safe
+import 'package:app_flowy/home/presentation/widgets/menu/hom_menu_size.dart';
+import 'package:expandable/expandable.dart';
+import 'package:flowy_infra/size.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class AppWidget extends StatelessWidget {
+  final App app;
+  const AppWidget(this.app, {Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Container();
+  }
+
+  ExpandableNotifier expandableWrapper(BuildContext context, Widget child) {
+    return ExpandableNotifier(
+      child: ScrollOnExpand(
+        scrollOnExpand: true,
+        scrollOnCollapse: false,
+        child: Card(
+          clipBehavior: Clip.antiAlias,
+          child: Column(
+            children: <Widget>[
+              ExpandablePanel(
+                theme: const ExpandableThemeData(
+                  headerAlignment: ExpandablePanelHeaderAlignment.center,
+                  tapBodyToExpand: false,
+                  tapBodyToCollapse: false,
+                  iconPadding: EdgeInsets.zero,
+                  hasIcon: false,
+                ),
+                header: AppHeader(app),
+                expanded: Padding(
+                  padding: EdgeInsets.only(left: Sizes.iconMed),
+                  child: child,
+                ),
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}
+
+class AppHeader extends StatelessWidget {
+  final App app;
+  const AppHeader(
+    this.app, {
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      color: Colors.white,
+      child: Padding(
+        padding: EdgeInsets.symmetric(vertical: Insets.m),
+        child: Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            ExpandableIcon(
+              theme: ExpandableThemeData(
+                expandIcon: Icons.arrow_right,
+                collapseIcon: Icons.arrow_drop_down,
+                iconColor: Colors.black,
+                iconSize: HomeMenuSize.collapseIconSize,
+                iconPadding: EdgeInsets.zero,
+                hasIcon: false,
+              ),
+            ),
+            Expanded(
+              child: Text(app.name),
+            ),
+            SizedBox(
+              height: HomeMenuSize.createViewButtonSize,
+              child: createViewPopupMenu(context),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget createViewPopupMenu(BuildContext context) {
+    return PopupMenuButton(
+        iconSize: 24,
+        tooltip: 'create new view',
+        icon: const Icon(Icons.add),
+        padding: EdgeInsets.zero,
+        onSelected: (viewType) =>
+            handleCreateView(viewType as ViewType, context),
+        itemBuilder: (context) => menuItemBuilder());
+  }
+
+  List<PopupMenuEntry> menuItemBuilder() {
+    return ViewType.values
+        // .where((element) => element != ViewType.ViewTypeUnknown)
+        .map((ty) {
+      return PopupMenuItem<ViewType>(
+          value: ty,
+          child: Row(
+            children: <Widget>[Text(ty.name)],
+          ));
+    }).toList();
+  }
+
+  void handleCreateView(ViewType viewType, BuildContext context) {
+    switch (viewType) {
+      case ViewType.Docs:
+        // context
+        //     .read<AppEditBloc>()
+        //     .add(AppEditEvent.createView(app.id, 'Grid View'));
+        break;
+    }
+  }
+}

+ 0 - 0
app_flowy/lib/home/presentation/widgets/app/new_app.dart


+ 0 - 0
app_flowy/lib/home/presentation/widgets/app/view_list.dart


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

@@ -19,17 +19,20 @@ import 'package:textstyle_extensions/textstyle_extensions.dart';
 class HomeMenu extends StatelessWidget {
   final Function(Option<PageContext>) pageContextChanged;
   final Function(bool) isCollapseChanged;
+  final String workspaceId;
 
   const HomeMenu(
       {Key? key,
       required this.pageContextChanged,
-      required this.isCollapseChanged})
+      required this.isCollapseChanged,
+      required this.workspaceId})
       : super(key: key);
 
   @override
   Widget build(BuildContext context) {
     return BlocProvider(
-      create: (context) => getIt<MenuBloc>(),
+      create: (context) =>
+          getIt<MenuBloc>(param1: workspaceId)..add(const MenuEvent.initial()),
       child: MultiBlocListener(
         listeners: [
           BlocListener<MenuBloc, MenuState>(

+ 1 - 1
app_flowy/lib/main.dart

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

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

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

+ 13 - 0
app_flowy/packages/flowy_infra_ui/lib/widget/error_page.dart

@@ -0,0 +1,13 @@
+import 'package:flutter/material.dart';
+
+class FlowyErrorPage extends StatelessWidget {
+  final String error;
+  const FlowyErrorPage(this.error, {Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      child: Text(error),
+    );
+  }
+}

+ 16 - 14
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbenum.dart

@@ -9,24 +9,26 @@
 import 'dart:core' as $core;
 import 'package:protobuf/protobuf.dart' as $pb;
 
-class WorkspaceObservableType extends $pb.ProtobufEnum {
-  static const WorkspaceObservableType Unknown = WorkspaceObservableType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
-  static const WorkspaceObservableType WorkspaceUpdated = WorkspaceObservableType._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceUpdated');
-  static const WorkspaceObservableType AppDescUpdated = WorkspaceObservableType._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppDescUpdated');
-  static const WorkspaceObservableType AppViewsUpdated = WorkspaceObservableType._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppViewsUpdated');
-  static const WorkspaceObservableType ViewUpdated = WorkspaceObservableType._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewUpdated');
+class WorkspaceObservable extends $pb.ProtobufEnum {
+  static const WorkspaceObservable Unknown = WorkspaceObservable._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
+  static const WorkspaceObservable WorkspaceUpdateDesc = WorkspaceObservable._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceUpdateDesc');
+  static const WorkspaceObservable WorkspaceAddApp = WorkspaceObservable._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceAddApp');
+  static const WorkspaceObservable AppUpdateDesc = WorkspaceObservable._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppUpdateDesc');
+  static const WorkspaceObservable AppAddView = WorkspaceObservable._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppAddView');
+  static const WorkspaceObservable ViewUpdateDesc = WorkspaceObservable._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewUpdateDesc');
 
-  static const $core.List<WorkspaceObservableType> values = <WorkspaceObservableType> [
+  static const $core.List<WorkspaceObservable> values = <WorkspaceObservable> [
     Unknown,
-    WorkspaceUpdated,
-    AppDescUpdated,
-    AppViewsUpdated,
-    ViewUpdated,
+    WorkspaceUpdateDesc,
+    WorkspaceAddApp,
+    AppUpdateDesc,
+    AppAddView,
+    ViewUpdateDesc,
   ];
 
-  static final $core.Map<$core.int, WorkspaceObservableType> _byValue = $pb.ProtobufEnum.initByValue(values);
-  static WorkspaceObservableType? valueOf($core.int value) => _byValue[value];
+  static final $core.Map<$core.int, WorkspaceObservable> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static WorkspaceObservable? valueOf($core.int value) => _byValue[value];
 
-  const WorkspaceObservableType._($core.int v, $core.String n) : super(v, n);
+  const WorkspaceObservable._($core.int v, $core.String n) : super(v, n);
 }
 

+ 10 - 9
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbjson.dart

@@ -8,17 +8,18 @@
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
 import 'dart:typed_data' as $typed_data;
-@$core.Deprecated('Use workspaceObservableTypeDescriptor instead')
-const WorkspaceObservableType$json = const {
-  '1': 'WorkspaceObservableType',
+@$core.Deprecated('Use workspaceObservableDescriptor instead')
+const WorkspaceObservable$json = const {
+  '1': 'WorkspaceObservable',
   '2': const [
     const {'1': 'Unknown', '2': 0},
-    const {'1': 'WorkspaceUpdated', '2': 10},
-    const {'1': 'AppDescUpdated', '2': 20},
-    const {'1': 'AppViewsUpdated', '2': 21},
-    const {'1': 'ViewUpdated', '2': 30},
+    const {'1': 'WorkspaceUpdateDesc', '2': 10},
+    const {'1': 'WorkspaceAddApp', '2': 11},
+    const {'1': 'AppUpdateDesc', '2': 20},
+    const {'1': 'AppAddView', '2': 21},
+    const {'1': 'ViewUpdateDesc', '2': 30},
   ],
 };
 
-/// Descriptor for `WorkspaceObservableType`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List workspaceObservableTypeDescriptor = $convert.base64Decode('ChdXb3Jrc3BhY2VPYnNlcnZhYmxlVHlwZRILCgdVbmtub3duEAASFAoQV29ya3NwYWNlVXBkYXRlZBAKEhIKDkFwcERlc2NVcGRhdGVkEBQSEwoPQXBwVmlld3NVcGRhdGVkEBUSDwoLVmlld1VwZGF0ZWQQHg==');
+/// Descriptor for `WorkspaceObservable`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List workspaceObservableDescriptor = $convert.base64Decode('ChNXb3Jrc3BhY2VPYnNlcnZhYmxlEgsKB1Vua25vd24QABIXChNXb3Jrc3BhY2VVcGRhdGVEZXNjEAoSEwoPV29ya3NwYWNlQWRkQXBwEAsSEQoNQXBwVXBkYXRlRGVzYxAUEg4KCkFwcEFkZFZpZXcQFRISCg5WaWV3VXBkYXRlRGVzYxAe');

+ 8 - 8
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pb.dart

@@ -29,7 +29,7 @@ class CreateViewRequest extends $pb.GeneratedMessage {
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
     ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
     ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
-    ..e<ViewTypeIdentifier>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewType', $pb.PbFieldType.OE, defaultOrMaker: ViewTypeIdentifier.Docs, valueOf: ViewTypeIdentifier.valueOf, enumValues: ViewTypeIdentifier.values)
+    ..e<ViewType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewType', $pb.PbFieldType.OE, defaultOrMaker: ViewType.Docs, valueOf: ViewType.valueOf, enumValues: ViewType.values)
     ..hasRequiredFields = false
   ;
 
@@ -39,7 +39,7 @@ class CreateViewRequest extends $pb.GeneratedMessage {
     $core.String? name,
     $core.String? desc,
     $core.String? thumbnail,
-    ViewTypeIdentifier? viewType,
+    ViewType? viewType,
   }) {
     final _result = create();
     if (appId != null) {
@@ -120,9 +120,9 @@ class CreateViewRequest extends $pb.GeneratedMessage {
   void clearThumbnail() => clearField(4);
 
   @$pb.TagNumber(5)
-  ViewTypeIdentifier get viewType => $_getN(4);
+  ViewType get viewType => $_getN(4);
   @$pb.TagNumber(5)
-  set viewType(ViewTypeIdentifier v) { setField(5, v); }
+  set viewType(ViewType v) { setField(5, v); }
   @$pb.TagNumber(5)
   $core.bool hasViewType() => $_has(4);
   @$pb.TagNumber(5)
@@ -135,7 +135,7 @@ class View extends $pb.GeneratedMessage {
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'appId')
     ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
     ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
-    ..e<ViewTypeIdentifier>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewType', $pb.PbFieldType.OE, defaultOrMaker: ViewTypeIdentifier.Docs, valueOf: ViewTypeIdentifier.valueOf, enumValues: ViewTypeIdentifier.values)
+    ..e<ViewType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewType', $pb.PbFieldType.OE, defaultOrMaker: ViewType.Docs, valueOf: ViewType.valueOf, enumValues: ViewType.values)
     ..hasRequiredFields = false
   ;
 
@@ -145,7 +145,7 @@ class View extends $pb.GeneratedMessage {
     $core.String? appId,
     $core.String? name,
     $core.String? desc,
-    ViewTypeIdentifier? viewType,
+    ViewType? viewType,
   }) {
     final _result = create();
     if (id != null) {
@@ -223,9 +223,9 @@ class View extends $pb.GeneratedMessage {
   void clearDesc() => clearField(4);
 
   @$pb.TagNumber(5)
-  ViewTypeIdentifier get viewType => $_getN(4);
+  ViewType get viewType => $_getN(4);
   @$pb.TagNumber(5)
-  set viewType(ViewTypeIdentifier v) { setField(5, v); }
+  set viewType(ViewType v) { setField(5, v); }
   @$pb.TagNumber(5)
   $core.bool hasViewType() => $_has(4);
   @$pb.TagNumber(5)

+ 6 - 6
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pbenum.dart

@@ -9,16 +9,16 @@
 import 'dart:core' as $core;
 import 'package:protobuf/protobuf.dart' as $pb;
 
-class ViewTypeIdentifier extends $pb.ProtobufEnum {
-  static const ViewTypeIdentifier Docs = ViewTypeIdentifier._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Docs');
+class ViewType extends $pb.ProtobufEnum {
+  static const ViewType Docs = ViewType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Docs');
 
-  static const $core.List<ViewTypeIdentifier> values = <ViewTypeIdentifier> [
+  static const $core.List<ViewType> values = <ViewType> [
     Docs,
   ];
 
-  static final $core.Map<$core.int, ViewTypeIdentifier> _byValue = $pb.ProtobufEnum.initByValue(values);
-  static ViewTypeIdentifier? valueOf($core.int value) => _byValue[value];
+  static final $core.Map<$core.int, ViewType> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static ViewType? valueOf($core.int value) => _byValue[value];
 
-  const ViewTypeIdentifier._($core.int v, $core.String n) : super(v, n);
+  const ViewType._($core.int v, $core.String n) : super(v, n);
 }
 

+ 9 - 9
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pbjson.dart

@@ -8,16 +8,16 @@
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
 import 'dart:typed_data' as $typed_data;
-@$core.Deprecated('Use viewTypeIdentifierDescriptor instead')
-const ViewTypeIdentifier$json = const {
-  '1': 'ViewTypeIdentifier',
+@$core.Deprecated('Use viewTypeDescriptor instead')
+const ViewType$json = const {
+  '1': 'ViewType',
   '2': const [
     const {'1': 'Docs', '2': 0},
   ],
 };
 
-/// Descriptor for `ViewTypeIdentifier`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List viewTypeIdentifierDescriptor = $convert.base64Decode('ChJWaWV3VHlwZUlkZW50aWZpZXISCAoERG9jcxAA');
+/// Descriptor for `ViewType`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List viewTypeDescriptor = $convert.base64Decode('CghWaWV3VHlwZRIICgREb2NzEAA=');
 @$core.Deprecated('Use createViewRequestDescriptor instead')
 const CreateViewRequest$json = const {
   '1': 'CreateViewRequest',
@@ -26,7 +26,7 @@ const CreateViewRequest$json = const {
     const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
     const {'1': 'desc', '3': 3, '4': 1, '5': 9, '10': 'desc'},
     const {'1': 'thumbnail', '3': 4, '4': 1, '5': 9, '9': 0, '10': 'thumbnail'},
-    const {'1': 'view_type', '3': 5, '4': 1, '5': 14, '6': '.ViewTypeIdentifier', '10': 'viewType'},
+    const {'1': 'view_type', '3': 5, '4': 1, '5': 14, '6': '.ViewType', '10': 'viewType'},
   ],
   '8': const [
     const {'1': 'one_of_thumbnail'},
@@ -34,7 +34,7 @@ const CreateViewRequest$json = const {
 };
 
 /// Descriptor for `CreateViewRequest`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List createViewRequestDescriptor = $convert.base64Decode('ChFDcmVhdGVWaWV3UmVxdWVzdBIVCgZhcHBfaWQYASABKAlSBWFwcElkEhIKBG5hbWUYAiABKAlSBG5hbWUSEgoEZGVzYxgDIAEoCVIEZGVzYxIeCgl0aHVtYm5haWwYBCABKAlIAFIJdGh1bWJuYWlsEjAKCXZpZXdfdHlwZRgFIAEoDjITLlZpZXdUeXBlSWRlbnRpZmllclIIdmlld1R5cGVCEgoQb25lX29mX3RodW1ibmFpbA==');
+final $typed_data.Uint8List createViewRequestDescriptor = $convert.base64Decode('ChFDcmVhdGVWaWV3UmVxdWVzdBIVCgZhcHBfaWQYASABKAlSBWFwcElkEhIKBG5hbWUYAiABKAlSBG5hbWUSEgoEZGVzYxgDIAEoCVIEZGVzYxIeCgl0aHVtYm5haWwYBCABKAlIAFIJdGh1bWJuYWlsEiYKCXZpZXdfdHlwZRgFIAEoDjIJLlZpZXdUeXBlUgh2aWV3VHlwZUISChBvbmVfb2ZfdGh1bWJuYWls');
 @$core.Deprecated('Use viewDescriptor instead')
 const View$json = const {
   '1': 'View',
@@ -43,12 +43,12 @@ const View$json = const {
     const {'1': 'app_id', '3': 2, '4': 1, '5': 9, '10': 'appId'},
     const {'1': 'name', '3': 3, '4': 1, '5': 9, '10': 'name'},
     const {'1': 'desc', '3': 4, '4': 1, '5': 9, '10': 'desc'},
-    const {'1': 'view_type', '3': 5, '4': 1, '5': 14, '6': '.ViewTypeIdentifier', '10': 'viewType'},
+    const {'1': 'view_type', '3': 5, '4': 1, '5': 14, '6': '.ViewType', '10': 'viewType'},
   ],
 };
 
 /// Descriptor for `View`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List viewDescriptor = $convert.base64Decode('CgRWaWV3Eg4KAmlkGAEgASgJUgJpZBIVCgZhcHBfaWQYAiABKAlSBWFwcElkEhIKBG5hbWUYAyABKAlSBG5hbWUSEgoEZGVzYxgEIAEoCVIEZGVzYxIwCgl2aWV3X3R5cGUYBSABKA4yEy5WaWV3VHlwZUlkZW50aWZpZXJSCHZpZXdUeXBl');
+final $typed_data.Uint8List viewDescriptor = $convert.base64Decode('CgRWaWV3Eg4KAmlkGAEgASgJUgJpZBIVCgZhcHBfaWQYAiABKAlSBWFwcElkEhIKBG5hbWUYAyABKAlSBG5hbWUSEgoEZGVzYxgEIAEoCVIEZGVzYxImCgl2aWV3X3R5cGUYBSABKA4yCS5WaWV3VHlwZVIIdmlld1R5cGU=');
 @$core.Deprecated('Use repeatedViewDescriptor instead')
 const RepeatedView$json = const {
   '1': 'RepeatedView',

+ 5 - 5
app_flowy/pubspec.lock

@@ -14,7 +14,7 @@ packages:
       name: analyzer
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.7.1"
+    version: "1.7.2"
   animations:
     dependency: transitive
     description:
@@ -35,7 +35,7 @@ packages:
       name: async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.6.1"
+    version: "2.7.0"
   bloc:
     dependency: transitive
     description:
@@ -119,7 +119,7 @@ packages:
       name: charcode
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.0"
+    version: "1.3.1"
   checked_yaml:
     dependency: transitive
     description:
@@ -456,7 +456,7 @@ packages:
       name: meta
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.3.0"
+    version: "1.7.0"
   mime:
     dependency: transitive
     description:
@@ -701,7 +701,7 @@ packages:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.3.0"
+    version: "0.4.1"
   textstyle_extensions:
     dependency: transitive
     description:

+ 6 - 1
rust-lib/dart-ffi/src/model/ffi_response.rs

@@ -1,5 +1,5 @@
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
-use flowy_dispatch::prelude::{EventResponse, Payload, StatusCode};
+use flowy_dispatch::prelude::{DispatchError, EventResponse, Payload, StatusCode};
 
 #[derive(ProtoBuf_Enum, Clone, Copy)]
 pub enum FFIStatusCode {
@@ -33,6 +33,11 @@ impl std::convert::From<EventResponse> for FFIResponse {
             StatusCode::Err => FFIStatusCode::Err,
         };
 
+        // let msg = match resp.error {
+        //     None => "".to_owned(),
+        //     Some(e) => format!("{:?}", e),
+        // };
+
         FFIResponse { payload, code }
     }
 }

+ 2 - 2
rust-lib/flowy-derive/src/derive_cache/derive_cache.rs

@@ -43,10 +43,10 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         | "SignInParams"
         | "UserError"
         => TypeCategory::Protobuf,
-        "ViewTypeIdentifier"
+        "ViewType"
         | "WorkspaceEvent"
         | "WorkspaceErrorCode"
-        | "WorkspaceObservableType"
+        | "WorkspaceObservable"
         | "FFIStatusCode"
         | "UserStatus"
         | "UserEvent"

+ 0 - 1
rust-lib/flowy-derive/src/proto_buf/deserialize.rs

@@ -88,7 +88,6 @@ fn token_stream_for_one_of(ctxt: &Ctxt, field: &ASTField) -> Option<TokenStream>
         },
         TypeCategory::Array => {
             let take_func = format_ident!("take_{}", ident.to_string());
-            let ty = bracketed_ty_info.unwrap().ty;
             Some(quote! {
                 if pb.#has_func() {
                     o.#member=Some(pb.#take_func());

+ 10 - 1
rust-lib/flowy-dispatch/src/dispatch.rs

@@ -155,6 +155,7 @@ impl Service<DispatchContext> for DispatchService {
 
         Box::pin(async move {
             let result = {
+                // print_module_map_info(&module_map);
                 match module_map.get(&request.event) {
                     Some(module) => {
                         let fut = module.new_service(());
@@ -163,7 +164,7 @@ impl Service<DispatchContext> for DispatchService {
                     },
                     None => {
                         let msg = format!("Can not find the event handler. {:?}", request);
-                        log::trace!("{}", msg);
+                        log::error!("{}", msg);
                         Err(InternalError::new(msg).into())
                     },
                 }
@@ -180,6 +181,7 @@ impl Service<DispatchContext> for DispatchService {
     }
 }
 
+#[allow(dead_code)]
 fn module_info(modules: &Vec<Module>) -> String {
     let mut info = format!("{} modules loaded\n", modules.len());
     for module in modules {
@@ -187,3 +189,10 @@ fn module_info(modules: &Vec<Module>) -> String {
     }
     info
 }
+
+#[allow(dead_code)]
+fn print_module_map_info(module_map: &ModuleMap) {
+    module_map.iter().for_each(|(k, v)| {
+        log::info!("Event: {:?} module: {:?}", k, v.name);
+    })
+}

+ 1 - 1
rust-lib/flowy-observable/src/dart/stream_sender.rs

@@ -14,7 +14,7 @@ impl RustStreamSender {
     fn new() -> Self { Self { isolate: None } }
 
     fn inner_set_port(&mut self, port: i64) {
-        log::debug!("Setup rust to flutter stream with port {}", port);
+        log::info!("Setup rust to flutter stream with port {}", port);
         self.isolate = Some(allo_isolate::Isolate::new(port));
     }
 

+ 2 - 2
rust-lib/flowy-sdk/Cargo.toml

@@ -7,8 +7,8 @@ edition = "2018"
 
 [dependencies]
 flowy-dispatch = { path = "../flowy-dispatch", features = ["use_tracing"]}
-flowy-log = { path = "../flowy-log" }
-#flowy-log = { path = "../flowy-log", features = ["use_bunyan"] }
+#flowy-log = { path = "../flowy-log" }
+flowy-log = { path = "../flowy-log", features = ["use_bunyan"] }
 flowy-user = { path = "../flowy-user" }
 flowy-infra = { path = "../flowy-infra" }
 flowy-workspace = { path = "../flowy-workspace" }

+ 1 - 0
rust-lib/flowy-workspace/Cargo.toml

@@ -25,6 +25,7 @@ serde = { version = "1.0", features = ["derive"] }
 derive_more = {version = "0.99", features = ["display"]}
 bincode = { version = "1.3"}
 unicode-segmentation = "1.7.1"
+tracing = { version = "0.1", features = ["log"] }
 
 [dev-dependencies]
 flowy-test = { path = "../flowy-test" }

+ 4 - 4
rust-lib/flowy-workspace/src/entities/view/parser/view_type.rs

@@ -1,12 +1,12 @@
-use crate::{entities::view::ViewTypeIdentifier, sql_tables::view::ViewType};
+use crate::{entities::view::ViewType, sql_tables::view::ViewTableType};
 
 #[derive(Debug)]
-pub struct ViewTypeCheck(pub ViewType);
+pub struct ViewTypeCheck(pub ViewTableType);
 
 impl ViewTypeCheck {
-    pub fn parse(s: ViewTypeIdentifier) -> Result<ViewTypeCheck, String> {
+    pub fn parse(s: ViewType) -> Result<ViewTypeCheck, String> {
         match s {
-            ViewTypeIdentifier::Docs => Ok(Self(ViewType::Docs)),
+            ViewType::Docs => Ok(Self(ViewTableType::Docs)),
         }
     }
 }

+ 7 - 7
rust-lib/flowy-workspace/src/entities/view/view_create.rs

@@ -2,18 +2,18 @@ use crate::{
     entities::{app::parser::AppId, view::parser::*},
     errors::{ErrorBuilder, WorkspaceError, WorkspaceErrorCode},
     impl_def_and_def_mut,
-    sql_tables::view::ViewType,
+    sql_tables::view::ViewTableType,
 };
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use std::convert::TryInto;
 
 #[derive(PartialEq, Debug, ProtoBuf_Enum)]
-pub enum ViewTypeIdentifier {
+pub enum ViewType {
     Docs = 0,
 }
 
-impl std::default::Default for ViewTypeIdentifier {
-    fn default() -> Self { ViewTypeIdentifier::Docs }
+impl std::default::Default for ViewType {
+    fn default() -> Self { ViewType::Docs }
 }
 
 #[derive(Default, ProtoBuf)]
@@ -31,7 +31,7 @@ pub struct CreateViewRequest {
     pub thumbnail: Option<String>,
 
     #[pb(index = 5)]
-    pub view_type: ViewTypeIdentifier,
+    pub view_type: ViewType,
 }
 
 pub struct CreateViewParams {
@@ -39,7 +39,7 @@ pub struct CreateViewParams {
     pub name: String,
     pub desc: String,
     pub thumbnail: String,
-    pub view_type: ViewType,
+    pub view_type: ViewTableType,
 }
 
 impl TryInto<CreateViewParams> for CreateViewRequest {
@@ -101,7 +101,7 @@ pub struct View {
     pub desc: String,
 
     #[pb(index = 5)]
-    pub view_type: ViewTypeIdentifier,
+    pub view_type: ViewType,
 }
 
 #[derive(PartialEq, Debug, Default, ProtoBuf)]

+ 6 - 6
rust-lib/flowy-workspace/src/event.rs

@@ -4,27 +4,27 @@ use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
 #[event_err = "WorkspaceError"]
 pub enum WorkspaceEvent {
-    #[display(fmt = "Create workspace")]
+    #[display(fmt = "CreateWorkspace")]
     #[event(input = "CreateWorkspaceRequest", output = "Workspace")]
     CreateWorkspace = 0,
 
-    #[display(fmt = "Get user's current workspace")]
+    #[display(fmt = "GetCurWorkspace")]
     #[event(output = "Workspace")]
     GetCurWorkspace = 1,
 
-    #[display(fmt = "Get user's workspace")]
+    #[display(fmt = "GetWorkspace")]
     #[event(input = "QueryWorkspaceRequest", output = "Workspace")]
     GetWorkspace    = 2,
 
-    #[display(fmt = "Create app information")]
+    #[display(fmt = "CreateApp")]
     #[event(input = "CreateAppRequest", output = "App")]
     CreateApp       = 101,
 
-    #[display(fmt = "Get app information")]
+    #[display(fmt = "GetApp")]
     #[event(input = "QueryAppRequest", output = "App")]
     GetApp          = 102,
 
-    #[display(fmt = "Create view")]
+    #[display(fmt = "CreateView")]
     #[event(input = "CreateViewRequest", output = "View")]
     CreateView      = 201,
 }

+ 2 - 0
rust-lib/flowy-workspace/src/handlers/app_handler.rs

@@ -9,6 +9,7 @@ use crate::{
 use flowy_dispatch::prelude::{response_ok, Data, ModuleData, ResponseResult};
 use std::{convert::TryInto, sync::Arc};
 
+#[tracing::instrument(name = "create_app", skip(data, controller))]
 pub async fn create_app(
     data: Data<CreateAppRequest>,
     controller: ModuleData<Arc<AppController>>,
@@ -18,6 +19,7 @@ pub async fn create_app(
     response_ok(detail)
 }
 
+#[tracing::instrument(name = "get_app", skip(data, controller))]
 pub async fn get_app(
     data: Data<QueryAppRequest>,
     controller: ModuleData<Arc<AppController>>,

+ 3 - 0
rust-lib/flowy-workspace/src/handlers/workspace_handler.rs

@@ -6,6 +6,7 @@ use crate::{
 use flowy_dispatch::prelude::{response_ok, Data, ModuleData, ResponseResult};
 use std::{convert::TryInto, sync::Arc};
 
+#[tracing::instrument(name = "create_workspace", skip(data, controller))]
 pub async fn create_workspace(
     data: Data<CreateWorkspaceRequest>,
     controller: ModuleData<Arc<WorkspaceController>>,
@@ -16,6 +17,7 @@ pub async fn create_workspace(
     response_ok(detail)
 }
 
+#[tracing::instrument(name = "get_cur_workspace", skip(controller))]
 pub async fn get_cur_workspace(
     controller: ModuleData<Arc<WorkspaceController>>,
 ) -> ResponseResult<Workspace, WorkspaceError> {
@@ -23,6 +25,7 @@ pub async fn get_cur_workspace(
     response_ok(workspace)
 }
 
+#[tracing::instrument(name = "get_workspace", skip(data, controller))]
 pub async fn get_workspace(
     data: Data<QueryWorkspaceRequest>,
     controller: ModuleData<Arc<WorkspaceController>>,

+ 16 - 12
rust-lib/flowy-workspace/src/observable/observable.rs

@@ -4,26 +4,30 @@ use flowy_observable::{dart::RustStreamSender, entities::ObservableSubject};
 const OBSERVABLE_CATEGORY: &'static str = "Workspace";
 
 #[derive(ProtoBuf_Enum, Debug)]
-pub(crate) enum WorkspaceObservableType {
-    Unknown          = 0,
-    WorkspaceUpdated = 10,
-    AppDescUpdated   = 20,
-    AppViewsUpdated  = 21,
-    ViewUpdated      = 30,
+pub(crate) enum WorkspaceObservable {
+    Unknown             = 0,
+
+    WorkspaceUpdateDesc = 10,
+    WorkspaceAddApp     = 11,
+
+    AppUpdateDesc       = 20,
+    AppAddView          = 21,
+
+    ViewUpdateDesc      = 30,
 }
 
-impl std::default::Default for WorkspaceObservableType {
-    fn default() -> Self { WorkspaceObservableType::Unknown }
+impl std::default::Default for WorkspaceObservable {
+    fn default() -> Self { WorkspaceObservable::Unknown }
 }
 
 pub(crate) struct ObservableSender {
-    ty: WorkspaceObservableType,
+    ty: WorkspaceObservable,
     subject_id: String,
     payload: Option<Vec<u8>>,
 }
 
 impl ObservableSender {
-    pub(crate) fn new(subject_id: &str, ty: WorkspaceObservableType) -> Self {
+    pub(crate) fn new(subject_id: &str, ty: WorkspaceObservable) -> Self {
         Self {
             subject_id: subject_id.to_owned(),
             ty,
@@ -60,11 +64,11 @@ impl ObservableSender {
     }
 }
 
-pub(crate) fn send_observable(id: &str, ty: WorkspaceObservableType) {
+pub(crate) fn send_observable(id: &str, ty: WorkspaceObservable) {
     ObservableSender::new(id, ty).send();
 }
 
-pub(crate) fn send_observable_with_payload<T>(id: &str, ty: WorkspaceObservableType, payload: T)
+pub(crate) fn send_observable_with_payload<T>(id: &str, ty: WorkspaceObservable, payload: T)
 where
     T: ToBytes,
 {

+ 45 - 39
rust-lib/flowy-workspace/src/protobuf/model/observable.rs

@@ -24,37 +24,40 @@
 // const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
 
 #[derive(Clone,PartialEq,Eq,Debug,Hash)]
-pub enum WorkspaceObservableType {
+pub enum WorkspaceObservable {
     Unknown = 0,
-    WorkspaceUpdated = 10,
-    AppDescUpdated = 20,
-    AppViewsUpdated = 21,
-    ViewUpdated = 30,
+    WorkspaceUpdateDesc = 10,
+    WorkspaceAddApp = 11,
+    AppUpdateDesc = 20,
+    AppAddView = 21,
+    ViewUpdateDesc = 30,
 }
 
-impl ::protobuf::ProtobufEnum for WorkspaceObservableType {
+impl ::protobuf::ProtobufEnum for WorkspaceObservable {
     fn value(&self) -> i32 {
         *self as i32
     }
 
-    fn from_i32(value: i32) -> ::std::option::Option<WorkspaceObservableType> {
+    fn from_i32(value: i32) -> ::std::option::Option<WorkspaceObservable> {
         match value {
-            0 => ::std::option::Option::Some(WorkspaceObservableType::Unknown),
-            10 => ::std::option::Option::Some(WorkspaceObservableType::WorkspaceUpdated),
-            20 => ::std::option::Option::Some(WorkspaceObservableType::AppDescUpdated),
-            21 => ::std::option::Option::Some(WorkspaceObservableType::AppViewsUpdated),
-            30 => ::std::option::Option::Some(WorkspaceObservableType::ViewUpdated),
+            0 => ::std::option::Option::Some(WorkspaceObservable::Unknown),
+            10 => ::std::option::Option::Some(WorkspaceObservable::WorkspaceUpdateDesc),
+            11 => ::std::option::Option::Some(WorkspaceObservable::WorkspaceAddApp),
+            20 => ::std::option::Option::Some(WorkspaceObservable::AppUpdateDesc),
+            21 => ::std::option::Option::Some(WorkspaceObservable::AppAddView),
+            30 => ::std::option::Option::Some(WorkspaceObservable::ViewUpdateDesc),
             _ => ::std::option::Option::None
         }
     }
 
     fn values() -> &'static [Self] {
-        static values: &'static [WorkspaceObservableType] = &[
-            WorkspaceObservableType::Unknown,
-            WorkspaceObservableType::WorkspaceUpdated,
-            WorkspaceObservableType::AppDescUpdated,
-            WorkspaceObservableType::AppViewsUpdated,
-            WorkspaceObservableType::ViewUpdated,
+        static values: &'static [WorkspaceObservable] = &[
+            WorkspaceObservable::Unknown,
+            WorkspaceObservable::WorkspaceUpdateDesc,
+            WorkspaceObservable::WorkspaceAddApp,
+            WorkspaceObservable::AppUpdateDesc,
+            WorkspaceObservable::AppAddView,
+            WorkspaceObservable::ViewUpdateDesc,
         ];
         values
     }
@@ -62,43 +65,46 @@ impl ::protobuf::ProtobufEnum for WorkspaceObservableType {
     fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
         static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
         descriptor.get(|| {
-            ::protobuf::reflect::EnumDescriptor::new_pb_name::<WorkspaceObservableType>("WorkspaceObservableType", file_descriptor_proto())
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<WorkspaceObservable>("WorkspaceObservable", file_descriptor_proto())
         })
     }
 }
 
-impl ::std::marker::Copy for WorkspaceObservableType {
+impl ::std::marker::Copy for WorkspaceObservable {
 }
 
-impl ::std::default::Default for WorkspaceObservableType {
+impl ::std::default::Default for WorkspaceObservable {
     fn default() -> Self {
-        WorkspaceObservableType::Unknown
+        WorkspaceObservable::Unknown
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for WorkspaceObservableType {
+impl ::protobuf::reflect::ProtobufValue for WorkspaceObservable {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
     }
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x10observable.proto*v\n\x17WorkspaceObservableType\x12\x0b\n\x07Unkno\
-    wn\x10\0\x12\x14\n\x10WorkspaceUpdated\x10\n\x12\x12\n\x0eAppDescUpdated\
-    \x10\x14\x12\x13\n\x0fAppViewsUpdated\x10\x15\x12\x0f\n\x0bViewUpdated\
-    \x10\x1eJ\xf7\x01\n\x06\x12\x04\0\0\x08\x01\n\x08\n\x01\x0c\x12\x03\0\0\
-    \x12\n\n\n\x02\x05\0\x12\x04\x02\0\x08\x01\n\n\n\x03\x05\0\x01\x12\x03\
-    \x02\x05\x1c\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x10\n\x0c\n\x05\x05\
-    \0\x02\0\x01\x12\x03\x03\x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\
-    \x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x1a\n\x0c\n\x05\x05\0\
-    \x02\x01\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\
-    \x04\x17\x19\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\x18\n\x0c\n\x05\
-    \x05\0\x02\x02\x01\x12\x03\x05\x04\x12\n\x0c\n\x05\x05\0\x02\x02\x02\x12\
-    \x03\x05\x15\x17\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\x04\x19\n\x0c\n\
-    \x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x13\n\x0c\n\x05\x05\0\x02\x03\x02\
-    \x12\x03\x06\x16\x18\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x07\x04\x15\n\x0c\
-    \n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x0f\n\x0c\n\x05\x05\0\x02\x04\
-    \x02\x12\x03\x07\x12\x14b\x06proto3\
+    \n\x10observable.proto*\x87\x01\n\x13WorkspaceObservable\x12\x0b\n\x07Un\
+    known\x10\0\x12\x17\n\x13WorkspaceUpdateDesc\x10\n\x12\x13\n\x0fWorkspac\
+    eAddApp\x10\x0b\x12\x11\n\rAppUpdateDesc\x10\x14\x12\x0e\n\nAppAddView\
+    \x10\x15\x12\x12\n\x0eViewUpdateDesc\x10\x1eJ\xa0\x02\n\x06\x12\x04\0\0\
+    \t\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\t\
+    \x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x18\n\x0b\n\x04\x05\0\x02\0\x12\
+    \x03\x03\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x0b\n\x0c\n\
+    \x05\x05\0\x02\0\x02\x12\x03\x03\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\
+    \x03\x04\x04\x1d\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x17\n\x0c\
+    \n\x05\x05\0\x02\x01\x02\x12\x03\x04\x1a\x1c\n\x0b\n\x04\x05\0\x02\x02\
+    \x12\x03\x05\x04\x19\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x13\n\
+    \x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x05\x16\x18\n\x0b\n\x04\x05\0\x02\
+    \x03\x12\x03\x06\x04\x17\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\
+    \x11\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\x06\x14\x16\n\x0b\n\x04\x05\0\
+    \x02\x04\x12\x03\x07\x04\x14\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\
+    \x04\x0e\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x07\x11\x13\n\x0b\n\x04\
+    \x05\0\x02\x05\x12\x03\x08\x04\x18\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\
+    \x08\x04\x12\n\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x08\x15\x17b\x06proto\
+    3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 77 - 77
rust-lib/flowy-workspace/src/protobuf/model/view_create.rs

@@ -29,7 +29,7 @@ pub struct CreateViewRequest {
     pub app_id: ::std::string::String,
     pub name: ::std::string::String,
     pub desc: ::std::string::String,
-    pub view_type: ViewTypeIdentifier,
+    pub view_type: ViewType,
     // message oneof groups
     pub one_of_thumbnail: ::std::option::Option<CreateViewRequest_oneof_one_of_thumbnail>,
     // special fields
@@ -180,18 +180,18 @@ impl CreateViewRequest {
         }
     }
 
-    // .ViewTypeIdentifier view_type = 5;
+    // .ViewType view_type = 5;
 
 
-    pub fn get_view_type(&self) -> ViewTypeIdentifier {
+    pub fn get_view_type(&self) -> ViewType {
         self.view_type
     }
     pub fn clear_view_type(&mut self) {
-        self.view_type = ViewTypeIdentifier::Docs;
+        self.view_type = ViewType::Docs;
     }
 
     // Param is passed by value, moved
-    pub fn set_view_type(&mut self, v: ViewTypeIdentifier) {
+    pub fn set_view_type(&mut self, v: ViewType) {
         self.view_type = v;
     }
 }
@@ -244,7 +244,7 @@ impl ::protobuf::Message for CreateViewRequest {
         if !self.desc.is_empty() {
             my_size += ::protobuf::rt::string_size(3, &self.desc);
         }
-        if self.view_type != ViewTypeIdentifier::Docs {
+        if self.view_type != ViewType::Docs {
             my_size += ::protobuf::rt::enum_size(5, self.view_type);
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_thumbnail {
@@ -269,7 +269,7 @@ impl ::protobuf::Message for CreateViewRequest {
         if !self.desc.is_empty() {
             os.write_string(3, &self.desc)?;
         }
-        if self.view_type != ViewTypeIdentifier::Docs {
+        if self.view_type != ViewType::Docs {
             os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.view_type))?;
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_thumbnail {
@@ -337,7 +337,7 @@ impl ::protobuf::Message for CreateViewRequest {
                 CreateViewRequest::has_thumbnail,
                 CreateViewRequest::get_thumbnail,
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewTypeIdentifier>>(
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewType>>(
                 "view_type",
                 |m: &CreateViewRequest| { &m.view_type },
                 |m: &mut CreateViewRequest| { &mut m.view_type },
@@ -362,7 +362,7 @@ impl ::protobuf::Clear for CreateViewRequest {
         self.name.clear();
         self.desc.clear();
         self.one_of_thumbnail = ::std::option::Option::None;
-        self.view_type = ViewTypeIdentifier::Docs;
+        self.view_type = ViewType::Docs;
         self.unknown_fields.clear();
     }
 }
@@ -386,7 +386,7 @@ pub struct View {
     pub app_id: ::std::string::String,
     pub name: ::std::string::String,
     pub desc: ::std::string::String,
-    pub view_type: ViewTypeIdentifier,
+    pub view_type: ViewType,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -507,18 +507,18 @@ impl View {
         ::std::mem::replace(&mut self.desc, ::std::string::String::new())
     }
 
-    // .ViewTypeIdentifier view_type = 5;
+    // .ViewType view_type = 5;
 
 
-    pub fn get_view_type(&self) -> ViewTypeIdentifier {
+    pub fn get_view_type(&self) -> ViewType {
         self.view_type
     }
     pub fn clear_view_type(&mut self) {
-        self.view_type = ViewTypeIdentifier::Docs;
+        self.view_type = ViewType::Docs;
     }
 
     // Param is passed by value, moved
-    pub fn set_view_type(&mut self, v: ViewTypeIdentifier) {
+    pub fn set_view_type(&mut self, v: ViewType) {
         self.view_type = v;
     }
 }
@@ -571,7 +571,7 @@ impl ::protobuf::Message for View {
         if !self.desc.is_empty() {
             my_size += ::protobuf::rt::string_size(4, &self.desc);
         }
-        if self.view_type != ViewTypeIdentifier::Docs {
+        if self.view_type != ViewType::Docs {
             my_size += ::protobuf::rt::enum_size(5, self.view_type);
         }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
@@ -592,7 +592,7 @@ impl ::protobuf::Message for View {
         if !self.desc.is_empty() {
             os.write_string(4, &self.desc)?;
         }
-        if self.view_type != ViewTypeIdentifier::Docs {
+        if self.view_type != ViewType::Docs {
             os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.view_type))?;
         }
         os.write_unknown_fields(self.get_unknown_fields())?;
@@ -653,7 +653,7 @@ impl ::protobuf::Message for View {
                 |m: &View| { &m.desc },
                 |m: &mut View| { &mut m.desc },
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewTypeIdentifier>>(
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewType>>(
                 "view_type",
                 |m: &View| { &m.view_type },
                 |m: &mut View| { &mut m.view_type },
@@ -678,7 +678,7 @@ impl ::protobuf::Clear for View {
         self.app_id.clear();
         self.name.clear();
         self.desc.clear();
-        self.view_type = ViewTypeIdentifier::Docs;
+        self.view_type = ViewType::Docs;
         self.unknown_fields.clear();
     }
 }
@@ -862,25 +862,25 @@ impl ::protobuf::reflect::ProtobufValue for RepeatedView {
 }
 
 #[derive(Clone,PartialEq,Eq,Debug,Hash)]
-pub enum ViewTypeIdentifier {
+pub enum ViewType {
     Docs = 0,
 }
 
-impl ::protobuf::ProtobufEnum for ViewTypeIdentifier {
+impl ::protobuf::ProtobufEnum for ViewType {
     fn value(&self) -> i32 {
         *self as i32
     }
 
-    fn from_i32(value: i32) -> ::std::option::Option<ViewTypeIdentifier> {
+    fn from_i32(value: i32) -> ::std::option::Option<ViewType> {
         match value {
-            0 => ::std::option::Option::Some(ViewTypeIdentifier::Docs),
+            0 => ::std::option::Option::Some(ViewType::Docs),
             _ => ::std::option::Option::None
         }
     }
 
     fn values() -> &'static [Self] {
-        static values: &'static [ViewTypeIdentifier] = &[
-            ViewTypeIdentifier::Docs,
+        static values: &'static [ViewType] = &[
+            ViewType::Docs,
         ];
         values
     }
@@ -888,77 +888,77 @@ impl ::protobuf::ProtobufEnum for ViewTypeIdentifier {
     fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
         static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
         descriptor.get(|| {
-            ::protobuf::reflect::EnumDescriptor::new_pb_name::<ViewTypeIdentifier>("ViewTypeIdentifier", file_descriptor_proto())
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<ViewType>("ViewType", file_descriptor_proto())
         })
     }
 }
 
-impl ::std::marker::Copy for ViewTypeIdentifier {
+impl ::std::marker::Copy for ViewType {
 }
 
-impl ::std::default::Default for ViewTypeIdentifier {
+impl ::std::default::Default for ViewType {
     fn default() -> Self {
-        ViewTypeIdentifier::Docs
+        ViewType::Docs
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for ViewTypeIdentifier {
+impl ::protobuf::reflect::ProtobufValue for ViewType {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
     }
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x11view_create.proto\"\xb8\x01\n\x11CreateViewRequest\x12\x15\n\x06ap\
+    \n\x11view_create.proto\"\xae\x01\n\x11CreateViewRequest\x12\x15\n\x06ap\
     p_id\x18\x01\x20\x01(\tR\x05appId\x12\x12\n\x04name\x18\x02\x20\x01(\tR\
     \x04name\x12\x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\x12\x1e\n\tthumbn\
-    ail\x18\x04\x20\x01(\tH\0R\tthumbnail\x120\n\tview_type\x18\x05\x20\x01(\
-    \x0e2\x13.ViewTypeIdentifierR\x08viewTypeB\x12\n\x10one_of_thumbnail\"\
-    \x87\x01\n\x04View\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x15\n\
-    \x06app_id\x18\x02\x20\x01(\tR\x05appId\x12\x12\n\x04name\x18\x03\x20\
-    \x01(\tR\x04name\x12\x12\n\x04desc\x18\x04\x20\x01(\tR\x04desc\x120\n\tv\
-    iew_type\x18\x05\x20\x01(\x0e2\x13.ViewTypeIdentifierR\x08viewType\"+\n\
-    \x0cRepeatedView\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.ViewR\x05i\
-    tems*\x1e\n\x12ViewTypeIdentifier\x12\x08\n\x04Docs\x10\0J\xa1\x06\n\x06\
-    \x12\x04\0\0\x15\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\
-    \x04\x02\0\x08\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x19\n\x0b\n\x04\
-    \x04\0\x02\0\x12\x03\x03\x04\x16\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\
-    \x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\x11\n\x0c\n\x05\x04\0\
-    \x02\0\x03\x12\x03\x03\x14\x15\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\
-    \x14\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\
-    \x02\x01\x01\x12\x03\x04\x0b\x0f\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\
-    \x04\x12\x13\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x14\n\x0c\n\x05\
-    \x04\0\x02\x02\x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\0\x02\x02\x01\x12\
-    \x03\x05\x0b\x0f\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x12\x13\n\x0b\
-    \n\x04\x04\0\x08\0\x12\x03\x06\x044\n\x0c\n\x05\x04\0\x08\0\x01\x12\x03\
-    \x06\n\x1a\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x1d2\n\x0c\n\x05\x04\0\
-    \x02\x03\x05\x12\x03\x06\x1d#\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06$\
-    -\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x0601\n\x0b\n\x04\x04\0\x02\x04\
-    \x12\x03\x07\x04%\n\x0c\n\x05\x04\0\x02\x04\x06\x12\x03\x07\x04\x16\n\
-    \x0c\n\x05\x04\0\x02\x04\x01\x12\x03\x07\x17\x20\n\x0c\n\x05\x04\0\x02\
-    \x04\x03\x12\x03\x07#$\n\n\n\x02\x04\x01\x12\x04\t\0\x0f\x01\n\n\n\x03\
-    \x04\x01\x01\x12\x03\t\x08\x0c\n\x0b\n\x04\x04\x01\x02\0\x12\x03\n\x04\
-    \x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\n\x04\n\n\x0c\n\x05\x04\x01\
-    \x02\0\x01\x12\x03\n\x0b\r\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\n\x10\
-    \x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x0b\x04\x16\n\x0c\n\x05\x04\x01\
-    \x02\x01\x05\x12\x03\x0b\x04\n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\
-    \x0b\x0b\x11\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x0b\x14\x15\n\x0b\n\
-    \x04\x04\x01\x02\x02\x12\x03\x0c\x04\x14\n\x0c\n\x05\x04\x01\x02\x02\x05\
-    \x12\x03\x0c\x04\n\n\x0c\n\x05\x04\x01\x02\x02\x01\x12\x03\x0c\x0b\x0f\n\
-    \x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\x0c\x12\x13\n\x0b\n\x04\x04\x01\
-    \x02\x03\x12\x03\r\x04\x14\n\x0c\n\x05\x04\x01\x02\x03\x05\x12\x03\r\x04\
-    \n\n\x0c\n\x05\x04\x01\x02\x03\x01\x12\x03\r\x0b\x0f\n\x0c\n\x05\x04\x01\
-    \x02\x03\x03\x12\x03\r\x12\x13\n\x0b\n\x04\x04\x01\x02\x04\x12\x03\x0e\
-    \x04%\n\x0c\n\x05\x04\x01\x02\x04\x06\x12\x03\x0e\x04\x16\n\x0c\n\x05\
-    \x04\x01\x02\x04\x01\x12\x03\x0e\x17\x20\n\x0c\n\x05\x04\x01\x02\x04\x03\
-    \x12\x03\x0e#$\n\n\n\x02\x04\x02\x12\x04\x10\0\x12\x01\n\n\n\x03\x04\x02\
-    \x01\x12\x03\x10\x08\x14\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x11\x04\x1c\n\
-    \x0c\n\x05\x04\x02\x02\0\x04\x12\x03\x11\x04\x0c\n\x0c\n\x05\x04\x02\x02\
-    \0\x06\x12\x03\x11\r\x11\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x11\x12\
-    \x17\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x11\x1a\x1b\n\n\n\x02\x05\0\
-    \x12\x04\x13\0\x15\x01\n\n\n\x03\x05\0\x01\x12\x03\x13\x05\x17\n\x0b\n\
-    \x04\x05\0\x02\0\x12\x03\x14\x04\r\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\
-    \x14\x04\x08\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x14\x0b\x0cb\x06proto3\
+    ail\x18\x04\x20\x01(\tH\0R\tthumbnail\x12&\n\tview_type\x18\x05\x20\x01(\
+    \x0e2\t.ViewTypeR\x08viewTypeB\x12\n\x10one_of_thumbnail\"}\n\x04View\
+    \x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x15\n\x06app_id\x18\x02\
+    \x20\x01(\tR\x05appId\x12\x12\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\
+    \x12\n\x04desc\x18\x04\x20\x01(\tR\x04desc\x12&\n\tview_type\x18\x05\x20\
+    \x01(\x0e2\t.ViewTypeR\x08viewType\"+\n\x0cRepeatedView\x12\x1b\n\x05ite\
+    ms\x18\x01\x20\x03(\x0b2\x05.ViewR\x05items*\x14\n\x08ViewType\x12\x08\n\
+    \x04Docs\x10\0J\xa1\x06\n\x06\x12\x04\0\0\x15\x01\n\x08\n\x01\x0c\x12\
+    \x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x08\x01\n\n\n\x03\x04\0\x01\
+    \x12\x03\x02\x08\x19\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x16\n\x0c\n\
+    \x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\
+    \x03\x03\x0b\x11\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x14\x15\n\x0b\n\
+    \x04\x04\0\x02\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x04\0\x02\x01\x05\x12\
+    \x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0b\x0f\n\x0c\n\
+    \x05\x04\0\x02\x01\x03\x12\x03\x04\x12\x13\n\x0b\n\x04\x04\0\x02\x02\x12\
+    \x03\x05\x04\x14\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x04\n\n\x0c\n\
+    \x05\x04\0\x02\x02\x01\x12\x03\x05\x0b\x0f\n\x0c\n\x05\x04\0\x02\x02\x03\
+    \x12\x03\x05\x12\x13\n\x0b\n\x04\x04\0\x08\0\x12\x03\x06\x044\n\x0c\n\
+    \x05\x04\0\x08\0\x01\x12\x03\x06\n\x1a\n\x0b\n\x04\x04\0\x02\x03\x12\x03\
+    \x06\x1d2\n\x0c\n\x05\x04\0\x02\x03\x05\x12\x03\x06\x1d#\n\x0c\n\x05\x04\
+    \0\x02\x03\x01\x12\x03\x06$-\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x0601\
+    \n\x0b\n\x04\x04\0\x02\x04\x12\x03\x07\x04\x1b\n\x0c\n\x05\x04\0\x02\x04\
+    \x06\x12\x03\x07\x04\x0c\n\x0c\n\x05\x04\0\x02\x04\x01\x12\x03\x07\r\x16\
+    \n\x0c\n\x05\x04\0\x02\x04\x03\x12\x03\x07\x19\x1a\n\n\n\x02\x04\x01\x12\
+    \x04\t\0\x0f\x01\n\n\n\x03\x04\x01\x01\x12\x03\t\x08\x0c\n\x0b\n\x04\x04\
+    \x01\x02\0\x12\x03\n\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\n\x04\
+    \n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\n\x0b\r\n\x0c\n\x05\x04\x01\x02\
+    \0\x03\x12\x03\n\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x0b\x04\x16\
+    \n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x0b\x04\n\n\x0c\n\x05\x04\x01\
+    \x02\x01\x01\x12\x03\x0b\x0b\x11\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\
+    \x0b\x14\x15\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\x0c\x04\x14\n\x0c\n\x05\
+    \x04\x01\x02\x02\x05\x12\x03\x0c\x04\n\n\x0c\n\x05\x04\x01\x02\x02\x01\
+    \x12\x03\x0c\x0b\x0f\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\x0c\x12\x13\
+    \n\x0b\n\x04\x04\x01\x02\x03\x12\x03\r\x04\x14\n\x0c\n\x05\x04\x01\x02\
+    \x03\x05\x12\x03\r\x04\n\n\x0c\n\x05\x04\x01\x02\x03\x01\x12\x03\r\x0b\
+    \x0f\n\x0c\n\x05\x04\x01\x02\x03\x03\x12\x03\r\x12\x13\n\x0b\n\x04\x04\
+    \x01\x02\x04\x12\x03\x0e\x04\x1b\n\x0c\n\x05\x04\x01\x02\x04\x06\x12\x03\
+    \x0e\x04\x0c\n\x0c\n\x05\x04\x01\x02\x04\x01\x12\x03\x0e\r\x16\n\x0c\n\
+    \x05\x04\x01\x02\x04\x03\x12\x03\x0e\x19\x1a\n\n\n\x02\x04\x02\x12\x04\
+    \x10\0\x12\x01\n\n\n\x03\x04\x02\x01\x12\x03\x10\x08\x14\n\x0b\n\x04\x04\
+    \x02\x02\0\x12\x03\x11\x04\x1c\n\x0c\n\x05\x04\x02\x02\0\x04\x12\x03\x11\
+    \x04\x0c\n\x0c\n\x05\x04\x02\x02\0\x06\x12\x03\x11\r\x11\n\x0c\n\x05\x04\
+    \x02\x02\0\x01\x12\x03\x11\x12\x17\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\
+    \x11\x1a\x1b\n\n\n\x02\x05\0\x12\x04\x13\0\x15\x01\n\n\n\x03\x05\0\x01\
+    \x12\x03\x13\x05\r\n\x0b\n\x04\x05\0\x02\0\x12\x03\x14\x04\r\n\x0c\n\x05\
+    \x05\0\x02\0\x01\x12\x03\x14\x04\x08\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\
+    \x14\x0b\x0cb\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 6 - 5
rust-lib/flowy-workspace/src/protobuf/proto/observable.proto

@@ -1,9 +1,10 @@
 syntax = "proto3";
 
-enum WorkspaceObservableType {
+enum WorkspaceObservable {
     Unknown = 0;
-    WorkspaceUpdated = 10;
-    AppDescUpdated = 20;
-    AppViewsUpdated = 21;
-    ViewUpdated = 30;
+    WorkspaceUpdateDesc = 10;
+    WorkspaceAddApp = 11;
+    AppUpdateDesc = 20;
+    AppAddView = 21;
+    ViewUpdateDesc = 30;
 }

+ 3 - 3
rust-lib/flowy-workspace/src/protobuf/proto/view_create.proto

@@ -5,18 +5,18 @@ message CreateViewRequest {
     string name = 2;
     string desc = 3;
     oneof one_of_thumbnail { string thumbnail = 4; };
-    ViewTypeIdentifier view_type = 5;
+    ViewType view_type = 5;
 }
 message View {
     string id = 1;
     string app_id = 2;
     string name = 3;
     string desc = 4;
-    ViewTypeIdentifier view_type = 5;
+    ViewType view_type = 5;
 }
 message RepeatedView {
     repeated View items = 1;
 }
-enum ViewTypeIdentifier {
+enum ViewType {
     Docs = 0;
 }

+ 2 - 2
rust-lib/flowy-workspace/src/services/app_controller.rs

@@ -36,7 +36,7 @@ impl AppController {
         let app_table = AppTable::new(params);
         let app: App = app_table.clone().into();
         let _ = self.sql.write_app_table(app_table)?;
-        send_observable(&app.workspace_id, WorkspaceObservableType::WorkspaceUpdated);
+        send_observable(&app.workspace_id, WorkspaceObservable::WorkspaceAddApp);
         Ok(app)
     }
 
@@ -49,7 +49,7 @@ impl AppController {
         let changeset = AppTableChangeset::new(params);
         let app_id = changeset.id.clone();
         let _ = self.sql.update_app_table(changeset)?;
-        send_observable(&app_id, WorkspaceObservableType::AppDescUpdated);
+        send_observable(&app_id, WorkspaceObservable::AppUpdateDesc);
         Ok(())
     }
 

+ 2 - 0
rust-lib/flowy-workspace/src/services/view_controller.rs

@@ -2,6 +2,7 @@ use crate::{
     entities::view::{CreateViewParams, View},
     errors::WorkspaceError,
     module::WorkspaceDatabase,
+    observable::{send_observable, WorkspaceObservable},
     sql_tables::view::{ViewTable, ViewTableSql},
 };
 use std::sync::Arc;
@@ -21,6 +22,7 @@ impl ViewController {
         let view: View = view_table.clone().into();
         let _ = self.sql.write_view_table(view_table)?;
 
+        send_observable(&view.id, WorkspaceObservable::AppAddView);
         Ok(view)
     }
 }

+ 3 - 0
rust-lib/flowy-workspace/src/services/workspace_controller.rs

@@ -2,6 +2,7 @@ use crate::{
     entities::{app::App, workspace::*},
     errors::*,
     module::{WorkspaceDatabase, WorkspaceUser},
+    observable::{send_observable, WorkspaceObservable},
     services::AppController,
     sql_tables::workspace::{WorkspaceSql, WorkspaceTable, WorkspaceTableChangeset},
 };
@@ -42,8 +43,10 @@ impl WorkspaceController {
 
     pub fn update_workspace(&self, params: UpdateWorkspaceParams) -> Result<(), WorkspaceError> {
         let changeset = WorkspaceTableChangeset::new(params);
+        let workspace_id = changeset.id.clone();
         let _ = self.sql.update_workspace(changeset)?;
 
+        send_observable(&workspace_id, WorkspaceObservable::WorkspaceUpdateDesc);
         Ok(())
     }
 

+ 11 - 11
rust-lib/flowy-workspace/src/sql_tables/view/view_table.rs

@@ -1,5 +1,5 @@
 use crate::{
-    entities::view::{CreateViewParams, View, ViewTypeIdentifier},
+    entities::view::{CreateViewParams, View, ViewType},
     impl_sql_integer_expression,
     sql_tables::app::AppTable,
 };
@@ -18,7 +18,7 @@ pub(crate) struct ViewTable {
     pub modified_time: i64,
     pub create_time: i64,
     pub thumbnail: String,
-    pub view_type: ViewType,
+    pub view_type: ViewTableType,
     pub version: i64,
 }
 
@@ -43,7 +43,7 @@ impl ViewTable {
 impl std::convert::Into<View> for ViewTable {
     fn into(self) -> View {
         let view_type = match self.view_type {
-            ViewType::Docs => ViewTypeIdentifier::Docs,
+            ViewTableType::Docs => ViewType::Docs,
         };
 
         View {
@@ -79,28 +79,28 @@ impl ViewTableChangeset {
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)]
 #[repr(i32)]
 #[sql_type = "Integer"]
-pub enum ViewType {
+pub enum ViewTableType {
     Docs = 0,
 }
 
-impl std::default::Default for ViewType {
-    fn default() -> Self { ViewType::Docs }
+impl std::default::Default for ViewTableType {
+    fn default() -> Self { ViewTableType::Docs }
 }
 
-impl std::convert::From<i32> for ViewType {
+impl std::convert::From<i32> for ViewTableType {
     fn from(value: i32) -> Self {
         match value {
-            0 => ViewType::Docs,
+            0 => ViewTableType::Docs,
             o => {
                 log::error!("Unsupported view type {}, fallback to ViewType::Docs", o);
-                ViewType::Docs
+                ViewTableType::Docs
             },
         }
     }
 }
 
-impl ViewType {
+impl ViewTableType {
     pub fn value(&self) -> i32 { *self as i32 }
 }
 
-impl_sql_integer_expression!(ViewType);
+impl_sql_integer_expression!(ViewTableType);

+ 2 - 2
rust-lib/flowy-workspace/tests/event/app_test.rs

@@ -37,7 +37,7 @@ fn app_create_with_view_and_then_get_success() {
         name: "View A".to_string(),
         desc: "".to_string(),
         thumbnail: None,
-        view_type: ViewTypeIdentifier::Docs,
+        view_type: ViewType::Docs,
     };
 
     let request_b = CreateViewRequest {
@@ -45,7 +45,7 @@ fn app_create_with_view_and_then_get_success() {
         name: "View B".to_string(),
         desc: "".to_string(),
         thumbnail: None,
-        view_type: ViewTypeIdentifier::Docs,
+        view_type: ViewType::Docs,
     };
 
     let view_a = create_view(request_a);