Bläddra i källkod

replace ViewSectionItem stateful with bloc

appflowy 3 år sedan
förälder
incheckning
9898b8bd24
21 ändrade filer med 1007 tillägg och 801 borttagningar
  1. 4 4
      app_flowy/lib/workspace/application/menu/menu_listen.dart
  2. 25 10
      app_flowy/lib/workspace/application/view/view_bloc.dart
  3. 413 65
      app_flowy/lib/workspace/application/view/view_bloc.freezed.dart
  4. 43 0
      app_flowy/lib/workspace/application/view/view_edit_bloc.dart
  5. 332 0
      app_flowy/lib/workspace/application/view/view_edit_bloc.freezed.dart
  6. 0 44
      app_flowy/lib/workspace/application/view/view_list_bloc.dart
  7. 0 484
      app_flowy/lib/workspace/application/view/view_list_bloc.freezed.dart
  8. 8 4
      app_flowy/lib/workspace/domain/i_view.dart
  9. 6 11
      app_flowy/lib/workspace/domain/i_workspace.dart
  10. 8 6
      app_flowy/lib/workspace/domain/image.dart
  11. 10 8
      app_flowy/lib/workspace/domain/page_stack/page_stack.dart
  12. 9 16
      app_flowy/lib/workspace/infrastructure/deps_resolver.dart
  13. 4 4
      app_flowy/lib/workspace/infrastructure/i_app_impl.dart
  14. 2 2
      app_flowy/lib/workspace/infrastructure/i_user_impl.dart
  15. 16 8
      app_flowy/lib/workspace/infrastructure/i_view_impl.dart
  16. 7 10
      app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart
  17. 7 14
      app_flowy/lib/workspace/infrastructure/repos/app_repo.dart
  18. 29 6
      app_flowy/lib/workspace/infrastructure/repos/view_repo.dart
  19. 5 7
      app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart
  20. 67 74
      app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/item.dart
  21. 12 24
      app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/section.dart

+ 4 - 4
app_flowy/lib/workspace/application/menu/menu_listen.dart

@@ -10,14 +10,14 @@ import 'package:dartz/dartz.dart';
 part 'menu_listen.freezed.dart';
 
 class MenuListenBloc extends Bloc<MenuListenEvent, MenuListenState> {
-  final IWorkspaceWatch watch;
-  MenuListenBloc(this.watch) : super(const MenuListenState.initial());
+  final IWorkspaceListener listener;
+  MenuListenBloc(this.listener) : super(const MenuListenState.initial());
 
   @override
   Stream<MenuListenState> mapEventToState(MenuListenEvent event) async* {
     yield* event.map(
       started: (_) async* {
-        watch.startWatching(
+        listener.start(
           addAppCallback: (appsOrFail) => _handleAppsOrFail(appsOrFail),
         );
       },
@@ -32,7 +32,7 @@ class MenuListenBloc extends Bloc<MenuListenEvent, MenuListenState> {
 
   @override
   Future<void> close() async {
-    await watch.stopWatching();
+    await listener.stop();
     return super.close();
   }
 

+ 25 - 10
app_flowy/lib/workspace/application/view/view_bloc.dart

@@ -1,3 +1,4 @@
+import 'package:app_flowy/workspace/domain/view_edit.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
@@ -12,32 +13,46 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
 
   ViewBloc({
     required this.iViewImpl,
-  }) : super(ViewState.initial());
+  }) : super(ViewState.initial(iViewImpl.view));
 
   @override
   Stream<ViewState> mapEventToState(ViewEvent event) async* {
-    yield* event.map(initial: (_) async* {
-      yield state;
-    });
+    yield* event.map(
+      setIsSelected: (e) async* {
+        yield state.copyWith(isSelected: e.isSelected);
+      },
+      setIsEditing: (e) async* {
+        yield state.copyWith(isEditing: e.isEditing);
+      },
+      setAction: (e) async* {
+        yield state.copyWith(action: e.action);
+      },
+    );
   }
 }
 
 @freezed
 class ViewEvent with _$ViewEvent {
-  const factory ViewEvent.initial() = Initial;
+  const factory ViewEvent.setIsSelected(bool isSelected) = SetSelected;
+  const factory ViewEvent.setIsEditing(bool isEditing) = SetEditing;
+  const factory ViewEvent.setAction(Option<ViewAction> action) = SetAction;
 }
 
 @freezed
 class ViewState with _$ViewState {
   const factory ViewState({
-    required bool isLoading,
-    required Option<View> view,
+    required View view,
+    required bool isSelected,
+    required bool isEditing,
+    required Option<ViewAction> action,
     required Either<Unit, WorkspaceError> successOrFailure,
   }) = _ViewState;
 
-  factory ViewState.initial() => ViewState(
-        isLoading: false,
-        view: none(),
+  factory ViewState.initial(View view) => ViewState(
+        view: view,
+        isSelected: false,
+        isEditing: false,
+        action: none(),
         successOrFailure: left(unit),
       );
 }

+ 413 - 65
app_flowy/lib/workspace/application/view/view_bloc.freezed.dart

@@ -16,8 +16,22 @@ final _privateConstructorUsedError = UnsupportedError(
 class _$ViewEventTearOff {
   const _$ViewEventTearOff();
 
-  Initial initial() {
-    return const Initial();
+  SetSelected setIsSelected(bool isSelected) {
+    return SetSelected(
+      isSelected,
+    );
+  }
+
+  SetEditing setIsEditing(bool isEditing) {
+    return SetEditing(
+      isEditing,
+    );
+  }
+
+  SetAction setAction(Option<ViewAction> action) {
+    return SetAction(
+      action,
+    );
   }
 }
 
@@ -28,23 +42,31 @@ const $ViewEvent = _$ViewEventTearOff();
 mixin _$ViewEvent {
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
-    required TResult Function() initial,
+    required TResult Function(bool isSelected) setIsSelected,
+    required TResult Function(bool isEditing) setIsEditing,
+    required TResult Function(Option<ViewAction> action) setAction,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
-    TResult Function()? initial,
+    TResult Function(bool isSelected)? setIsSelected,
+    TResult Function(bool isEditing)? setIsEditing,
+    TResult Function(Option<ViewAction> action)? setAction,
     required TResult orElse(),
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
-    required TResult Function(Initial value) initial,
+    required TResult Function(SetSelected value) setIsSelected,
+    required TResult Function(SetEditing value) setIsEditing,
+    required TResult Function(SetAction value) setAction,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
-    TResult Function(Initial value)? initial,
+    TResult Function(SetSelected value)? setIsSelected,
+    TResult Function(SetEditing value)? setIsEditing,
+    TResult Function(SetAction value)? setAction,
     required TResult orElse(),
   }) =>
       throw _privateConstructorUsedError;
@@ -66,55 +88,323 @@ class _$ViewEventCopyWithImpl<$Res> implements $ViewEventCopyWith<$Res> {
 }
 
 /// @nodoc
-abstract class $InitialCopyWith<$Res> {
-  factory $InitialCopyWith(Initial value, $Res Function(Initial) then) =
-      _$InitialCopyWithImpl<$Res>;
+abstract class $SetSelectedCopyWith<$Res> {
+  factory $SetSelectedCopyWith(
+          SetSelected value, $Res Function(SetSelected) then) =
+      _$SetSelectedCopyWithImpl<$Res>;
+  $Res call({bool isSelected});
+}
+
+/// @nodoc
+class _$SetSelectedCopyWithImpl<$Res> extends _$ViewEventCopyWithImpl<$Res>
+    implements $SetSelectedCopyWith<$Res> {
+  _$SetSelectedCopyWithImpl(
+      SetSelected _value, $Res Function(SetSelected) _then)
+      : super(_value, (v) => _then(v as SetSelected));
+
+  @override
+  SetSelected get _value => super._value as SetSelected;
+
+  @override
+  $Res call({
+    Object? isSelected = freezed,
+  }) {
+    return _then(SetSelected(
+      isSelected == freezed
+          ? _value.isSelected
+          : isSelected // ignore: cast_nullable_to_non_nullable
+              as bool,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$SetSelected implements SetSelected {
+  const _$SetSelected(this.isSelected);
+
+  @override
+  final bool isSelected;
+
+  @override
+  String toString() {
+    return 'ViewEvent.setIsSelected(isSelected: $isSelected)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is SetSelected &&
+            (identical(other.isSelected, isSelected) ||
+                const DeepCollectionEquality()
+                    .equals(other.isSelected, isSelected)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(isSelected);
+
+  @JsonKey(ignore: true)
+  @override
+  $SetSelectedCopyWith<SetSelected> get copyWith =>
+      _$SetSelectedCopyWithImpl<SetSelected>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(bool isSelected) setIsSelected,
+    required TResult Function(bool isEditing) setIsEditing,
+    required TResult Function(Option<ViewAction> action) setAction,
+  }) {
+    return setIsSelected(isSelected);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(bool isSelected)? setIsSelected,
+    TResult Function(bool isEditing)? setIsEditing,
+    TResult Function(Option<ViewAction> action)? setAction,
+    required TResult orElse(),
+  }) {
+    if (setIsSelected != null) {
+      return setIsSelected(isSelected);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(SetSelected value) setIsSelected,
+    required TResult Function(SetEditing value) setIsEditing,
+    required TResult Function(SetAction value) setAction,
+  }) {
+    return setIsSelected(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(SetSelected value)? setIsSelected,
+    TResult Function(SetEditing value)? setIsEditing,
+    TResult Function(SetAction value)? setAction,
+    required TResult orElse(),
+  }) {
+    if (setIsSelected != null) {
+      return setIsSelected(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class SetSelected implements ViewEvent {
+  const factory SetSelected(bool isSelected) = _$SetSelected;
+
+  bool get isSelected => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $SetSelectedCopyWith<SetSelected> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $SetEditingCopyWith<$Res> {
+  factory $SetEditingCopyWith(
+          SetEditing value, $Res Function(SetEditing) then) =
+      _$SetEditingCopyWithImpl<$Res>;
+  $Res call({bool isEditing});
+}
+
+/// @nodoc
+class _$SetEditingCopyWithImpl<$Res> extends _$ViewEventCopyWithImpl<$Res>
+    implements $SetEditingCopyWith<$Res> {
+  _$SetEditingCopyWithImpl(SetEditing _value, $Res Function(SetEditing) _then)
+      : super(_value, (v) => _then(v as SetEditing));
+
+  @override
+  SetEditing get _value => super._value as SetEditing;
+
+  @override
+  $Res call({
+    Object? isEditing = freezed,
+  }) {
+    return _then(SetEditing(
+      isEditing == freezed
+          ? _value.isEditing
+          : isEditing // ignore: cast_nullable_to_non_nullable
+              as bool,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$SetEditing implements SetEditing {
+  const _$SetEditing(this.isEditing);
+
+  @override
+  final bool isEditing;
+
+  @override
+  String toString() {
+    return 'ViewEvent.setIsEditing(isEditing: $isEditing)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is SetEditing &&
+            (identical(other.isEditing, isEditing) ||
+                const DeepCollectionEquality()
+                    .equals(other.isEditing, isEditing)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(isEditing);
+
+  @JsonKey(ignore: true)
+  @override
+  $SetEditingCopyWith<SetEditing> get copyWith =>
+      _$SetEditingCopyWithImpl<SetEditing>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function(bool isSelected) setIsSelected,
+    required TResult Function(bool isEditing) setIsEditing,
+    required TResult Function(Option<ViewAction> action) setAction,
+  }) {
+    return setIsEditing(isEditing);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function(bool isSelected)? setIsSelected,
+    TResult Function(bool isEditing)? setIsEditing,
+    TResult Function(Option<ViewAction> action)? setAction,
+    required TResult orElse(),
+  }) {
+    if (setIsEditing != null) {
+      return setIsEditing(isEditing);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(SetSelected value) setIsSelected,
+    required TResult Function(SetEditing value) setIsEditing,
+    required TResult Function(SetAction value) setAction,
+  }) {
+    return setIsEditing(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(SetSelected value)? setIsSelected,
+    TResult Function(SetEditing value)? setIsEditing,
+    TResult Function(SetAction value)? setAction,
+    required TResult orElse(),
+  }) {
+    if (setIsEditing != null) {
+      return setIsEditing(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class SetEditing implements ViewEvent {
+  const factory SetEditing(bool isEditing) = _$SetEditing;
+
+  bool get isEditing => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $SetEditingCopyWith<SetEditing> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $SetActionCopyWith<$Res> {
+  factory $SetActionCopyWith(SetAction value, $Res Function(SetAction) then) =
+      _$SetActionCopyWithImpl<$Res>;
+  $Res call({Option<ViewAction> action});
 }
 
 /// @nodoc
-class _$InitialCopyWithImpl<$Res> extends _$ViewEventCopyWithImpl<$Res>
-    implements $InitialCopyWith<$Res> {
-  _$InitialCopyWithImpl(Initial _value, $Res Function(Initial) _then)
-      : super(_value, (v) => _then(v as Initial));
+class _$SetActionCopyWithImpl<$Res> extends _$ViewEventCopyWithImpl<$Res>
+    implements $SetActionCopyWith<$Res> {
+  _$SetActionCopyWithImpl(SetAction _value, $Res Function(SetAction) _then)
+      : super(_value, (v) => _then(v as SetAction));
+
+  @override
+  SetAction get _value => super._value as SetAction;
 
   @override
-  Initial get _value => super._value as Initial;
+  $Res call({
+    Object? action = freezed,
+  }) {
+    return _then(SetAction(
+      action == freezed
+          ? _value.action
+          : action // ignore: cast_nullable_to_non_nullable
+              as Option<ViewAction>,
+    ));
+  }
 }
 
 /// @nodoc
 
-class _$Initial implements Initial {
-  const _$Initial();
+class _$SetAction implements SetAction {
+  const _$SetAction(this.action);
+
+  @override
+  final Option<ViewAction> action;
 
   @override
   String toString() {
-    return 'ViewEvent.initial()';
+    return 'ViewEvent.setAction(action: $action)';
   }
 
   @override
   bool operator ==(dynamic other) {
-    return identical(this, other) || (other is Initial);
+    return identical(this, other) ||
+        (other is SetAction &&
+            (identical(other.action, action) ||
+                const DeepCollectionEquality().equals(other.action, action)));
   }
 
   @override
-  int get hashCode => runtimeType.hashCode;
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(action);
+
+  @JsonKey(ignore: true)
+  @override
+  $SetActionCopyWith<SetAction> get copyWith =>
+      _$SetActionCopyWithImpl<SetAction>(this, _$identity);
 
   @override
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
-    required TResult Function() initial,
+    required TResult Function(bool isSelected) setIsSelected,
+    required TResult Function(bool isEditing) setIsEditing,
+    required TResult Function(Option<ViewAction> action) setAction,
   }) {
-    return initial();
+    return setAction(action);
   }
 
   @override
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
-    TResult Function()? initial,
+    TResult Function(bool isSelected)? setIsSelected,
+    TResult Function(bool isEditing)? setIsEditing,
+    TResult Function(Option<ViewAction> action)? setAction,
     required TResult orElse(),
   }) {
-    if (initial != null) {
-      return initial();
+    if (setAction != null) {
+      return setAction(action);
     }
     return orElse();
   }
@@ -122,26 +412,35 @@ class _$Initial implements Initial {
   @override
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
-    required TResult Function(Initial value) initial,
+    required TResult Function(SetSelected value) setIsSelected,
+    required TResult Function(SetEditing value) setIsEditing,
+    required TResult Function(SetAction value) setAction,
   }) {
-    return initial(this);
+    return setAction(this);
   }
 
   @override
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
-    TResult Function(Initial value)? initial,
+    TResult Function(SetSelected value)? setIsSelected,
+    TResult Function(SetEditing value)? setIsEditing,
+    TResult Function(SetAction value)? setAction,
     required TResult orElse(),
   }) {
-    if (initial != null) {
-      return initial(this);
+    if (setAction != null) {
+      return setAction(this);
     }
     return orElse();
   }
 }
 
-abstract class Initial implements ViewEvent {
-  const factory Initial() = _$Initial;
+abstract class SetAction implements ViewEvent {
+  const factory SetAction(Option<ViewAction> action) = _$SetAction;
+
+  Option<ViewAction> get action => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $SetActionCopyWith<SetAction> get copyWith =>
+      throw _privateConstructorUsedError;
 }
 
 /// @nodoc
@@ -149,12 +448,16 @@ class _$ViewStateTearOff {
   const _$ViewStateTearOff();
 
   _ViewState call(
-      {required bool isLoading,
-      required Option<View> view,
+      {required View view,
+      required bool isSelected,
+      required bool isEditing,
+      required Option<ViewAction> action,
       required Either<Unit, WorkspaceError> successOrFailure}) {
     return _ViewState(
-      isLoading: isLoading,
       view: view,
+      isSelected: isSelected,
+      isEditing: isEditing,
+      action: action,
       successOrFailure: successOrFailure,
     );
   }
@@ -165,8 +468,10 @@ const $ViewState = _$ViewStateTearOff();
 
 /// @nodoc
 mixin _$ViewState {
-  bool get isLoading => throw _privateConstructorUsedError;
-  Option<View> get view => throw _privateConstructorUsedError;
+  View get view => throw _privateConstructorUsedError;
+  bool get isSelected => throw _privateConstructorUsedError;
+  bool get isEditing => throw _privateConstructorUsedError;
+  Option<ViewAction> get action => throw _privateConstructorUsedError;
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
 
@@ -180,8 +485,10 @@ abstract class $ViewStateCopyWith<$Res> {
   factory $ViewStateCopyWith(ViewState value, $Res Function(ViewState) then) =
       _$ViewStateCopyWithImpl<$Res>;
   $Res call(
-      {bool isLoading,
-      Option<View> view,
+      {View view,
+      bool isSelected,
+      bool isEditing,
+      Option<ViewAction> action,
       Either<Unit, WorkspaceError> successOrFailure});
 }
 
@@ -195,19 +502,29 @@ class _$ViewStateCopyWithImpl<$Res> implements $ViewStateCopyWith<$Res> {
 
   @override
   $Res call({
-    Object? isLoading = freezed,
     Object? view = freezed,
+    Object? isSelected = freezed,
+    Object? isEditing = freezed,
+    Object? action = freezed,
     Object? successOrFailure = freezed,
   }) {
     return _then(_value.copyWith(
-      isLoading: isLoading == freezed
-          ? _value.isLoading
-          : isLoading // ignore: cast_nullable_to_non_nullable
-              as bool,
       view: view == freezed
           ? _value.view
           : view // ignore: cast_nullable_to_non_nullable
-              as Option<View>,
+              as View,
+      isSelected: isSelected == freezed
+          ? _value.isSelected
+          : isSelected // ignore: cast_nullable_to_non_nullable
+              as bool,
+      isEditing: isEditing == freezed
+          ? _value.isEditing
+          : isEditing // ignore: cast_nullable_to_non_nullable
+              as bool,
+      action: action == freezed
+          ? _value.action
+          : action // ignore: cast_nullable_to_non_nullable
+              as Option<ViewAction>,
       successOrFailure: successOrFailure == freezed
           ? _value.successOrFailure
           : successOrFailure // ignore: cast_nullable_to_non_nullable
@@ -223,8 +540,10 @@ abstract class _$ViewStateCopyWith<$Res> implements $ViewStateCopyWith<$Res> {
       __$ViewStateCopyWithImpl<$Res>;
   @override
   $Res call(
-      {bool isLoading,
-      Option<View> view,
+      {View view,
+      bool isSelected,
+      bool isEditing,
+      Option<ViewAction> action,
       Either<Unit, WorkspaceError> successOrFailure});
 }
 
@@ -239,19 +558,29 @@ class __$ViewStateCopyWithImpl<$Res> extends _$ViewStateCopyWithImpl<$Res>
 
   @override
   $Res call({
-    Object? isLoading = freezed,
     Object? view = freezed,
+    Object? isSelected = freezed,
+    Object? isEditing = freezed,
+    Object? action = freezed,
     Object? successOrFailure = freezed,
   }) {
     return _then(_ViewState(
-      isLoading: isLoading == freezed
-          ? _value.isLoading
-          : isLoading // ignore: cast_nullable_to_non_nullable
-              as bool,
       view: view == freezed
           ? _value.view
           : view // ignore: cast_nullable_to_non_nullable
-              as Option<View>,
+              as View,
+      isSelected: isSelected == freezed
+          ? _value.isSelected
+          : isSelected // ignore: cast_nullable_to_non_nullable
+              as bool,
+      isEditing: isEditing == freezed
+          ? _value.isEditing
+          : isEditing // ignore: cast_nullable_to_non_nullable
+              as bool,
+      action: action == freezed
+          ? _value.action
+          : action // ignore: cast_nullable_to_non_nullable
+              as Option<ViewAction>,
       successOrFailure: successOrFailure == freezed
           ? _value.successOrFailure
           : successOrFailure // ignore: cast_nullable_to_non_nullable
@@ -264,31 +593,42 @@ class __$ViewStateCopyWithImpl<$Res> extends _$ViewStateCopyWithImpl<$Res>
 
 class _$_ViewState implements _ViewState {
   const _$_ViewState(
-      {required this.isLoading,
-      required this.view,
+      {required this.view,
+      required this.isSelected,
+      required this.isEditing,
+      required this.action,
       required this.successOrFailure});
 
   @override
-  final bool isLoading;
+  final View view;
+  @override
+  final bool isSelected;
+  @override
+  final bool isEditing;
   @override
-  final Option<View> view;
+  final Option<ViewAction> action;
   @override
   final Either<Unit, WorkspaceError> successOrFailure;
 
   @override
   String toString() {
-    return 'ViewState(isLoading: $isLoading, view: $view, successOrFailure: $successOrFailure)';
+    return 'ViewState(view: $view, isSelected: $isSelected, isEditing: $isEditing, action: $action, successOrFailure: $successOrFailure)';
   }
 
   @override
   bool operator ==(dynamic other) {
     return identical(this, other) ||
         (other is _ViewState &&
-            (identical(other.isLoading, isLoading) ||
-                const DeepCollectionEquality()
-                    .equals(other.isLoading, isLoading)) &&
             (identical(other.view, view) ||
                 const DeepCollectionEquality().equals(other.view, view)) &&
+            (identical(other.isSelected, isSelected) ||
+                const DeepCollectionEquality()
+                    .equals(other.isSelected, isSelected)) &&
+            (identical(other.isEditing, isEditing) ||
+                const DeepCollectionEquality()
+                    .equals(other.isEditing, isEditing)) &&
+            (identical(other.action, action) ||
+                const DeepCollectionEquality().equals(other.action, action)) &&
             (identical(other.successOrFailure, successOrFailure) ||
                 const DeepCollectionEquality()
                     .equals(other.successOrFailure, successOrFailure)));
@@ -297,8 +637,10 @@ class _$_ViewState implements _ViewState {
   @override
   int get hashCode =>
       runtimeType.hashCode ^
-      const DeepCollectionEquality().hash(isLoading) ^
       const DeepCollectionEquality().hash(view) ^
+      const DeepCollectionEquality().hash(isSelected) ^
+      const DeepCollectionEquality().hash(isEditing) ^
+      const DeepCollectionEquality().hash(action) ^
       const DeepCollectionEquality().hash(successOrFailure);
 
   @JsonKey(ignore: true)
@@ -309,14 +651,20 @@ class _$_ViewState implements _ViewState {
 
 abstract class _ViewState implements ViewState {
   const factory _ViewState(
-      {required bool isLoading,
-      required Option<View> view,
+      {required View view,
+      required bool isSelected,
+      required bool isEditing,
+      required Option<ViewAction> action,
       required Either<Unit, WorkspaceError> successOrFailure}) = _$_ViewState;
 
   @override
-  bool get isLoading => throw _privateConstructorUsedError;
+  View get view => throw _privateConstructorUsedError;
+  @override
+  bool get isSelected => throw _privateConstructorUsedError;
+  @override
+  bool get isEditing => throw _privateConstructorUsedError;
   @override
-  Option<View> get view => throw _privateConstructorUsedError;
+  Option<ViewAction> get action => throw _privateConstructorUsedError;
   @override
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;

+ 43 - 0
app_flowy/lib/workspace/application/view/view_edit_bloc.dart

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

+ 332 - 0
app_flowy/lib/workspace/application/view/view_edit_bloc.freezed.dart

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

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

@@ -1,44 +0,0 @@
-import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:dartz/dartz.dart';
-
-part 'view_list_bloc.freezed.dart';
-
-class ViewListBloc extends Bloc<ViewListEvent, ViewListState> {
-  final List<View> views;
-  ViewListBloc({required this.views}) : super(ViewListState.initial(views));
-
-  @override
-  Stream<ViewListState> mapEventToState(ViewListEvent event) async* {
-    yield* event.map(
-      initial: (s) async* {
-        yield ViewListState.initial(s.views);
-      },
-      openView: (s) async* {
-        yield state.copyWith(openedView: some(s.view.id));
-      },
-    );
-  }
-}
-
-@freezed
-class ViewListEvent with _$ViewListEvent {
-  const factory ViewListEvent.initial(List<View> views) = Initial;
-  const factory ViewListEvent.openView(View view) = OpenView;
-}
-
-@freezed
-class ViewListState with _$ViewListState {
-  const factory ViewListState({
-    required bool isLoading,
-    required Option<String> openedView,
-    required Option<List<View>> views,
-  }) = _ViewListState;
-
-  factory ViewListState.initial(List<View> views) => ViewListState(
-        isLoading: false,
-        openedView: none(),
-        views: some(views),
-      );
-}

+ 0 - 484
app_flowy/lib/workspace/application/view/view_list_bloc.freezed.dart

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

+ 8 - 4
app_flowy/lib/workspace/domain/i_view.dart

@@ -5,11 +5,15 @@ import 'package:dartz/dartz.dart';
 typedef ViewUpdatedCallback = void Function(Either<View, WorkspaceError>);
 
 abstract class IView {
-  Future<Either<View, WorkspaceError>> readView();
+  View get view;
+
+  Future<Either<Unit, WorkspaceError>> pushIntoTrash();
+
+  Future<Either<Unit, WorkspaceError>> rename(String newName);
 }
 
-abstract class IViewWatch {
-  void startWatching({ViewUpdatedCallback? updatedCallback});
+abstract class IViewListener {
+  void start({ViewUpdatedCallback? updatedCallback});
 
-  Future<void> stopWatching();
+  Future<void> stop();
 }

+ 6 - 11
app_flowy/lib/workspace/domain/i_workspace.dart

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

+ 8 - 6
app_flowy/lib/workspace/domain/image.dart

@@ -3,17 +3,19 @@ import 'package:flutter/material.dart';
 import 'package:flowy_infra/image.dart';
 
 AssetImage assetImageForViewType(ViewType type) {
-  final imageName = imageNameForViewType(type);
+  final imageName = _imageNameForViewType(type);
   return AssetImage('assets/images/$imageName');
 }
 
-Widget svgForViewType(ViewType type) {
-  final imageName = imageNameForViewType(type);
-  final Widget widget = svg(imageName);
-  return widget;
+extension SvgViewType on View {
+  Widget thumbnail() {
+    final imageName = _imageNameForViewType(viewType);
+    final Widget widget = svg(imageName);
+    return widget;
+  }
 }
 
-String imageNameForViewType(ViewType type) {
+String _imageNameForViewType(ViewType type) {
   switch (type) {
     case ViewType.Doc:
       return "file_icon";

+ 10 - 8
app_flowy/lib/workspace/domain/page_stack/page_stack.dart

@@ -34,14 +34,16 @@ abstract class HomeStackContext extends Equatable with NavigationItem {
   Widget render();
 }
 
-HomeStackContext stackCtxFromView(View view) {
-  switch (view.viewType) {
-    case ViewType.Blank:
-      return BlankStackContext();
-    case ViewType.Doc:
-      return DocStackContext(view: view);
-    default:
-      return BlankStackContext();
+extension ViewStackContext on View {
+  HomeStackContext intoStackContext() {
+    switch (viewType) {
+      case ViewType.Blank:
+        return BlankStackContext();
+      case ViewType.Doc:
+        return DocStackContext(view: this);
+      default:
+        return BlankStackContext();
+    }
   }
 }
 

+ 9 - 16
app_flowy/lib/workspace/infrastructure/deps_resolver.dart

@@ -6,7 +6,7 @@ import 'package:app_flowy/workspace/application/menu/menu_bloc.dart';
 import 'package:app_flowy/workspace/application/menu/menu_user_bloc.dart';
 import 'package:app_flowy/workspace/application/menu/menu_listen.dart';
 import 'package:app_flowy/workspace/application/view/view_bloc.dart';
-import 'package:app_flowy/workspace/application/view/view_list_bloc.dart';
+import 'package:app_flowy/workspace/application/view/view_edit_bloc.dart';
 import 'package:app_flowy/workspace/application/workspace/welcome_bloc.dart';
 import 'package:app_flowy/workspace/domain/i_doc.dart';
 import 'package:app_flowy/workspace/domain/i_view.dart';
@@ -33,31 +33,31 @@ class HomeDepsResolver {
     //App
     getIt.registerFactoryParam<IApp, String, void>((appId, _) => IAppImpl(repo: AppRepository(appId: appId)));
     getIt.registerFactoryParam<IAppListenr, String, void>(
-        (appId, _) => IAppWatchImpl(repo: AppWatchRepository(appId: appId)));
+        (appId, _) => IAppListenerhImpl(repo: AppListenerRepository(appId: appId)));
 
     //workspace
     getIt.registerFactoryParam<IWorkspace, UserProfile, String>(
         (user, workspaceId) => IWorkspaceImpl(repo: WorkspaceRepo(user: user, workspaceId: workspaceId)));
-    getIt.registerFactoryParam<IWorkspaceWatch, UserProfile, String>(
-        (user, workspaceId) => IWorkspaceWatchImpl(repo: WorkspaceWatchRepo(user: user, workspaceId: workspaceId)));
+    getIt.registerFactoryParam<IWorkspaceListener, UserProfile, String>((user, workspaceId) =>
+        IWorkspaceListenerImpl(repo: WorkspaceListenerRepo(user: user, workspaceId: workspaceId)));
 
     // View
     getIt.registerFactoryParam<IView, View, void>((view, _) => IViewImpl(repo: ViewRepository(view: view)));
-    getIt.registerFactoryParam<IViewWatch, View, void>(
-        (view, _) => IViewWatchImpl(repo: ViewWatchRepository(view: view)));
+    getIt.registerFactoryParam<IViewListener, View, void>(
+        (view, _) => IViewListenerImpl(repo: ViewListenerRepository(view: view)));
 
     // Doc
     getIt.registerFactoryParam<IDoc, String, void>((docId, _) => IDocImpl(repo: DocRepository(docId: docId)));
 
     // User
     getIt.registerFactoryParam<IUser, UserProfile, void>((user, _) => IUserImpl(repo: UserRepo(user: user)));
-    getIt.registerFactoryParam<IUserListener, UserProfile, void>((user, _) => IUserWatchImpl(user: user));
+    getIt.registerFactoryParam<IUserListener, UserProfile, void>((user, _) => IUserListenerImpl(user: user));
 
     //Menu Bloc
     getIt.registerFactoryParam<MenuBloc, UserProfile, String>(
         (user, workspaceId) => MenuBloc(getIt<IWorkspace>(param1: user, param2: workspaceId)));
     getIt.registerFactoryParam<MenuListenBloc, UserProfile, String>(
-        (user, workspaceId) => MenuListenBloc(getIt<IWorkspaceWatch>(param1: user, param2: workspaceId)));
+        (user, workspaceId) => MenuListenBloc(getIt<IWorkspaceListener>(param1: user, param2: workspaceId)));
 
     getIt.registerFactoryParam<MenuUserBloc, UserProfile, void>(
         (user, _) => MenuUserBloc(getIt<IUser>(param1: user), getIt<IUserListener>(param1: user)));
@@ -67,24 +67,17 @@ class HomeDepsResolver {
     getIt.registerFactoryParam<AppListenBloc, String, void>(
         (appId, _) => AppListenBloc(getIt<IAppListenr>(param1: appId)));
 
-    getIt
-        .registerFactoryParam<ViewBloc, String, void>((viewId, _) => ViewBloc(iViewImpl: getIt<IView>(param1: viewId)));
+    getIt.registerFactoryParam<ViewBloc, View, void>((view, _) => ViewBloc(iViewImpl: getIt<IView>(param1: view)));
 
     getIt.registerFactoryParam<DocBloc, String, void>((docId, _) => DocBloc(iDocImpl: getIt<IDoc>(param1: docId)));
 
     getIt.registerFactoryParam<DocEditBloc, String, void>((docId, _) => DocEditBloc(getIt<IDoc>(param1: docId)));
 
-    // editor
-    getIt.registerFactoryParam<ViewListBloc, List<View>, void>((views, _) => ViewListBloc(views: views));
-
     getIt.registerFactoryParam<WelcomeBloc, UserProfile, void>(
       (user, _) => WelcomeBloc(
         repo: UserRepo(user: user),
         watch: getIt<IUserListener>(param1: user),
       ),
     );
-
-    // getIt.registerFactoryParam<ViewBloc, String, void>(
-    //     (viewId, _) => ViewBloc(iViewImpl: getIt<IView>(param1: viewId)));
   }
 }

+ 4 - 4
app_flowy/lib/workspace/infrastructure/i_app_impl.dart

@@ -28,15 +28,15 @@ class IAppImpl extends IApp {
   }
 }
 
-class IAppWatchImpl extends IAppListenr {
-  AppWatchRepository repo;
-  IAppWatchImpl({
+class IAppListenerhImpl extends IAppListenr {
+  AppListenerRepository repo;
+  IAppListenerhImpl({
     required this.repo,
   });
 
   @override
   void start({AppCreateViewCallback? addViewCallback, AppUpdatedCallback? updatedCallback}) {
-    repo.startWatching(createView: addViewCallback, update: updatedCallback);
+    repo.startListen(createView: addViewCallback, update: updatedCallback);
   }
 
   @override

+ 2 - 2
app_flowy/lib/workspace/infrastructure/i_user_impl.dart

@@ -49,7 +49,7 @@ class IUserImpl extends IUser {
   }
 }
 
-class IUserWatchImpl extends IUserListener {
+class IUserListenerImpl extends IUserListener {
   StreamSubscription<ObservableSubject>? _subscription;
   WorkspacesUpdatedCallback? _workspacesUpdated;
   AuthChangedCallback? _authChanged;
@@ -58,7 +58,7 @@ class IUserWatchImpl extends IUserListener {
   late WorkspaceObservableParser _workspaceParser;
   late UserObservableParser _userParser;
   late UserProfile _user;
-  IUserWatchImpl({
+  IUserListenerImpl({
     required UserProfile user,
   }) {
     _user = user;

+ 16 - 8
app_flowy/lib/workspace/infrastructure/i_view_impl.dart

@@ -1,8 +1,8 @@
 import 'package:app_flowy/workspace/domain/i_view.dart';
 import 'package:app_flowy/workspace/infrastructure/repos/view_repo.dart';
-import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 
 class IViewImpl extends IView {
   ViewRepository repo;
@@ -10,24 +10,32 @@ class IViewImpl extends IView {
   IViewImpl({required this.repo});
 
   @override
-  Future<Either<View, WorkspaceError>> readView() {
-    return repo.readView();
+  View get view => repo.view;
+
+  @override
+  Future<Either<Unit, WorkspaceError>> pushIntoTrash() {
+    return repo.updateView(isTrash: true);
+  }
+
+  @override
+  Future<Either<Unit, WorkspaceError>> rename(String newName) {
+    return repo.updateView(name: newName);
   }
 }
 
-class IViewWatchImpl extends IViewWatch {
-  final ViewWatchRepository repo;
-  IViewWatchImpl({
+class IViewListenerImpl extends IViewListener {
+  final ViewListenerRepository repo;
+  IViewListenerImpl({
     required this.repo,
   });
 
   @override
-  void startWatching({ViewUpdatedCallback? updatedCallback}) {
+  void start({ViewUpdatedCallback? updatedCallback}) {
     repo.startWatching(update: updatedCallback);
   }
 
   @override
-  Future<void> stopWatching() async {
+  Future<void> stop() async {
     await repo.close();
   }
 }

+ 7 - 10
app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart

@@ -13,8 +13,7 @@ class IWorkspaceImpl extends IWorkspace {
   });
 
   @override
-  Future<Either<App, WorkspaceError>> createApp(
-      {required String name, String? desc}) {
+  Future<Either<App, WorkspaceError>> createApp({required String name, String? desc}) {
     return repo.createApp(name, desc ?? "");
   }
 
@@ -29,21 +28,19 @@ class IWorkspaceImpl extends IWorkspace {
   }
 }
 
-class IWorkspaceWatchImpl extends IWorkspaceWatch {
-  WorkspaceWatchRepo repo;
-  IWorkspaceWatchImpl({
+class IWorkspaceListenerImpl extends IWorkspaceListener {
+  WorkspaceListenerRepo repo;
+  IWorkspaceListenerImpl({
     required this.repo,
   });
 
   @override
-  void startWatching(
-      {WorkspaceCreateAppCallback? addAppCallback,
-      WorkspaceUpdatedCallback? updatedCallback}) {
-    repo.startWatching(createApp: addAppCallback, update: updatedCallback);
+  void start({WorkspaceCreateAppCallback? addAppCallback, WorkspaceUpdatedCallback? updatedCallback}) {
+    repo.startListen(createApp: addAppCallback, update: updatedCallback);
   }
 
   @override
-  Future<void> stopWatching() async {
+  Future<void> stop() async {
     await repo.close();
   }
 }

+ 7 - 14
app_flowy/lib/workspace/infrastructure/repos/app_repo.dart

@@ -28,8 +28,7 @@ class AppRepository {
     return WorkspaceEventReadApp(request).send();
   }
 
-  Future<Either<View, WorkspaceError>> createView(
-      String name, String desc, ViewType viewType) {
+  Future<Either<View, WorkspaceError>> createView(String name, String desc, ViewType viewType) {
     final request = CreateViewRequest.create()
       ..belongToId = appId
       ..name = name
@@ -53,7 +52,7 @@ class AppRepository {
   }
 }
 
-class AppWatchRepository {
+class AppListenerRepository {
   StreamSubscription<ObservableSubject>? _subscription;
   AppCreateViewCallback? _createView;
   AppDeleteViewCallback? _deleteView;
@@ -61,25 +60,19 @@ class AppWatchRepository {
   late WorkspaceObservableParser _extractor;
   String appId;
 
-  AppWatchRepository({
+  AppListenerRepository({
     required this.appId,
   });
 
-  void startWatching(
-      {AppCreateViewCallback? createView,
-      AppDeleteViewCallback? deleteView,
-      AppUpdatedCallback? update}) {
+  void startListen({AppCreateViewCallback? createView, AppDeleteViewCallback? deleteView, AppUpdatedCallback? update}) {
     _createView = createView;
     _deleteView = deleteView;
     _update = update;
-    _extractor =
-        WorkspaceObservableParser(id: appId, callback: _bservableCallback);
-    _subscription =
-        RustStreamReceiver.listen((observable) => _extractor.parse(observable));
+    _extractor = WorkspaceObservableParser(id: appId, callback: _bservableCallback);
+    _subscription = RustStreamReceiver.listen((observable) => _extractor.parse(observable));
   }
 
-  void _bservableCallback(
-      WorkspaceObservable ty, Either<Uint8List, WorkspaceError> result) {
+  void _bservableCallback(WorkspaceObservable ty, Either<Uint8List, WorkspaceError> result) {
     switch (ty) {
       case WorkspaceObservable.AppCreateView:
         if (_createView != null) {

+ 29 - 6
app_flowy/lib/workspace/infrastructure/repos/view_repo.dart

@@ -6,7 +6,9 @@ import 'package:flowy_sdk/protobuf/flowy-dart-notify/subject.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/observable.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_delete.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_query.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_update.pb.dart';
 import 'package:flowy_sdk/rust_stream.dart';
 
 import 'package:app_flowy/workspace/domain/i_view.dart';
@@ -23,15 +25,38 @@ class ViewRepository {
     final request = QueryViewRequest.create()..viewId = view.id;
     return WorkspaceEventReadView(request).send();
   }
+
+  Future<Either<Unit, WorkspaceError>> updateView({String? name, String? desc, bool? isTrash}) {
+    final request = UpdateViewRequest.create()..viewId = view.id;
+
+    if (name != null) {
+      request.name = name;
+    }
+
+    if (desc != null) {
+      request.desc = desc;
+    }
+
+    if (isTrash != null) {
+      request.isTrash = isTrash;
+    }
+
+    return WorkspaceEventUpdateView(request).send();
+  }
+
+  Future<Either<Unit, WorkspaceError>> delete() {
+    final request = DeleteViewRequest.create()..viewId = view.id;
+    return WorkspaceEventDeleteView(request).send();
+  }
 }
 
-class ViewWatchRepository {
+class ViewListenerRepository {
   StreamSubscription<ObservableSubject>? _subscription;
   ViewUpdatedCallback? _update;
   late WorkspaceObservableParser _extractor;
   View view;
 
-  ViewWatchRepository({
+  ViewListenerRepository({
     required this.view,
   });
 
@@ -46,12 +71,10 @@ class ViewWatchRepository {
       },
     );
 
-    _subscription =
-        RustStreamReceiver.listen((observable) => _extractor.parse(observable));
+    _subscription = RustStreamReceiver.listen((observable) => _extractor.parse(observable));
   }
 
-  void _handleObservableType(
-      WorkspaceObservable ty, Either<Uint8List, WorkspaceError> result) {
+  void _handleObservableType(WorkspaceObservable ty, Either<Uint8List, WorkspaceError> result) {
     switch (ty) {
       case WorkspaceObservable.ViewUpdated:
         if (_update != null) {

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

@@ -62,7 +62,7 @@ class WorkspaceRepo {
   }
 }
 
-class WorkspaceWatchRepo {
+class WorkspaceListenerRepo {
   StreamSubscription<ObservableSubject>? _subscription;
   WorkspaceCreateAppCallback? _createApp;
   WorkspaceDeleteAppCallback? _deleteApp;
@@ -71,12 +71,12 @@ class WorkspaceWatchRepo {
   final UserProfile user;
   final String workspaceId;
 
-  WorkspaceWatchRepo({
+  WorkspaceListenerRepo({
     required this.user,
     required this.workspaceId,
   });
 
-  void startWatching({
+  void startListen({
     WorkspaceCreateAppCallback? createApp,
     WorkspaceDeleteAppCallback? deleteApp,
     WorkspaceUpdatedCallback? update,
@@ -92,12 +92,10 @@ class WorkspaceWatchRepo {
       },
     );
 
-    _subscription =
-        RustStreamReceiver.listen((observable) => _extractor.parse(observable));
+    _subscription = RustStreamReceiver.listen((observable) => _extractor.parse(observable));
   }
 
-  void _handleObservableType(
-      WorkspaceObservable ty, Either<Uint8List, WorkspaceError> result) {
+  void _handleObservableType(WorkspaceObservable ty, Either<Uint8List, WorkspaceError> result) {
     switch (ty) {
       case WorkspaceObservable.WorkspaceUpdated:
         if (_update != null) {

+ 67 - 74
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/item.dart

@@ -1,3 +1,6 @@
+import 'package:app_flowy/startup/startup.dart';
+import 'package:app_flowy/workspace/application/view/view_bloc.dart';
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
 import 'package:dartz/dartz.dart' as dartz;
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
@@ -6,8 +9,10 @@ import 'package:flowy_infra_ui/style_widget/hover.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
+import 'package:flowy_log/flowy_log.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 
@@ -15,105 +20,92 @@ import 'package:app_flowy/workspace/domain/image.dart';
 import 'package:app_flowy/workspace/domain/view_edit.dart';
 import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/menu_app.dart';
 
-class ViewWidgetContext {
-  final View view;
-  ViewWidgetContext(this.view);
-  Key valueKey() => ValueKey("${view.id}${view.version}");
-}
-
-typedef OpenViewCallback = void Function(View);
-
 // ignore: must_be_immutable
-class ViewSectionItem extends StatefulWidget {
-  final ViewWidgetContext viewCtx;
-  final bool isSelected;
-  final OpenViewCallback onOpen;
+class ViewSectionItem extends StatelessWidget {
+  final ViewBloc bloc;
+  final void Function(View) onSelected;
 
   ViewSectionItem({
     Key? key,
-    required this.viewCtx,
-    required this.isSelected,
-    required this.onOpen,
-  }) : super(key: viewCtx.valueKey());
-
-  @override
-  State<ViewSectionItem> createState() => _ViewSectionItemState();
-}
-
-// [[Widget: LifeCycle]]
-// https://flutterbyexample.com/lesson/stateful-widget-lifecycle
-class _ViewSectionItemState extends State<ViewSectionItem> {
-  bool isOnSelected = false;
+    required View view,
+    required bool isSelected,
+    required this.onSelected,
+  })  : bloc = getIt<ViewBloc>(param1: view),
+        super(key: ValueKey(view.id)) {
+    bloc.add(ViewEvent.setIsSelected(isSelected));
+  }
 
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
-    final config = HoverDisplayConfig(hoverColor: theme.bg3);
-    return InkWell(
-      onTap: _openView(context),
-      child: FlowyHover(
-        config: config,
-        builder: (context, onHover) => _render(context, onHover, config),
-        isOnSelected: () => isOnSelected || widget.isSelected,
+
+    return BlocProvider.value(
+      value: bloc,
+      child: BlocListener<ViewBloc, ViewState>(
+        listenWhen: (p, c) => p.action != c.action,
+        listener: (context, state) {
+          state.action.fold(() => null, (action) {
+            Log.info('$action');
+          });
+        },
+        child: BlocBuilder<ViewBloc, ViewState>(
+          builder: (context, state) {
+            return InkWell(
+              onTap: () => onSelected(context.read<ViewBloc>().state.view),
+              child: FlowyHover(
+                config: HoverDisplayConfig(hoverColor: theme.bg3),
+                builder: (context, onHover) => _render(context, onHover),
+                isOnSelected: () => state.isEditing || state.isSelected,
+              ),
+            );
+          },
+        ),
       ),
     );
   }
 
-  Widget _render(BuildContext context, bool onHover, HoverDisplayConfig config) {
+  Widget _render(BuildContext context, bool onHover) {
+    final state = context.read<ViewBloc>().state;
     List<Widget> children = [
-      SizedBox(
-        width: 16,
-        height: 16,
-        child: svgForViewType(widget.viewCtx.view.viewType),
-      ),
+      SizedBox(width: 16, height: 16, child: state.view.thumbnail()),
       const HSpace(6),
-      FlowyText.regular(
-        widget.viewCtx.view.name,
-        fontSize: 12,
-      ),
+      FlowyText.regular(state.view.name, fontSize: 12),
     ];
 
-    if (onHover || isOnSelected) {
+    if (onHover || state.isEditing) {
       children.add(const Spacer());
       children.add(ViewDisclosureButton(
         onTap: () {
-          setState(
-            () => isOnSelected = true,
-          );
+          context.read<ViewBloc>().add(const ViewEvent.setIsEditing(true));
+          getIt<HomeStackManager>().setStack(state.view.intoStackContext());
         },
-        onSelected: (selected) {
-          selected.fold(() => null, (action) {
-            debugPrint('$action.name');
-          });
-          setState(() {
-            isOnSelected = false;
-          });
+        onSelected: (action) {
+          context.read<ViewBloc>().add(const ViewEvent.setIsEditing(false));
+          context.read<ViewBloc>().add(ViewEvent.setAction(action));
         },
       ));
     }
 
-    return Container(
+    return SizedBox(
+      height: 24,
       child: Row(children: children).padding(
         left: MenuAppSizes.expandedPadding,
         right: MenuAppSizes.expandedIconPadding,
       ),
-      height: 24,
-      alignment: Alignment.centerLeft,
     );
   }
-
-  Function() _openView(BuildContext context) {
-    return () => widget.onOpen(widget.viewCtx.view);
-  }
 }
 
+// [[Widget: LifeCycle]]
+// https://flutterbyexample.com/lesson/stateful-widget-lifecycle
+
 class ViewDisclosureButton extends StatelessWidget {
-  final Function(dartz.Option<ViewAction>) onSelected;
   final Function() onTap;
+  final Function(dartz.Option<ViewAction>) onSelected;
   const ViewDisclosureButton({
     Key? key,
-    required this.onSelected,
     required this.onTap,
+    required this.onSelected,
   }) : super(key: key);
 
   @override
@@ -140,16 +132,15 @@ class ViewActionList implements FlowyOverlayDelegate {
   const ViewActionList({required this.anchorContext, required this.onSelected});
 
   void show(BuildContext buildContext) {
-    final items = ViewAction.values.map((action) {
-      return ActionItem(
-          action: action,
-          onSelected: (action) {
-            FlowyOverlay.of(buildContext).remove(_identifier);
-            onSelected(dartz.some(action));
-          });
-    }).toList();
+    final items = ViewAction.values
+        .map((action) => ActionItem(
+            action: action,
+            onSelected: (action) {
+              FlowyOverlay.of(buildContext).remove(_identifier);
+              onSelected(dartz.some(action));
+            }))
+        .toList();
 
-    // TODO: make sure the delegate of this wouldn't cause retain cycle
     ListOverlay.showWithAnchor(
       buildContext,
       identifier: _identifier,
@@ -181,17 +172,19 @@ class ActionItem extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
-    final config = HoverDisplayConfig(hoverColor: theme.hover);
 
     return FlowyHover(
-      config: config,
+      config: HoverDisplayConfig(hoverColor: theme.hover),
       builder: (context, onHover) {
         return GestureDetector(
           onTap: () => onSelected(action),
           child: FlowyText.medium(
             action.name,
             fontSize: 12,
-          ).padding(horizontal: 10, vertical: 6),
+          ).padding(
+            horizontal: 10,
+            vertical: 6,
+          ),
         );
       },
     );

+ 12 - 24
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/section.dart

@@ -1,11 +1,8 @@
-import 'package:flowy_log/flowy_log.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:provider/provider.dart';
-import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
 import 'item.dart';
 
 class ViewListNotifier extends ChangeNotifier {
@@ -49,32 +46,26 @@ class ViewSection extends StatelessWidget {
   Widget build(BuildContext context) {
     // The ViewListNotifier will be updated after ViewListData changed passed by parent widget
     return ChangeNotifierProxyProvider<ViewListNotifier, ViewSectionNotifier>(
-      create: (_) => ViewSectionNotifier(
-        Provider.of<ViewListNotifier>(
+      create: (_) {
+        final views = Provider.of<ViewListNotifier>(
           context,
           listen: false,
-        ).items,
-      ),
+        ).items;
+        return ViewSectionNotifier(views);
+      },
       update: (_, notifier, controller) => controller!..update(notifier),
       child: Consumer(builder: (context, ViewSectionNotifier notifier, child) {
-        return _renderItems(context, notifier.views);
+        return _renderSectionItems(context, notifier.views);
       }),
     );
   }
 
-  Widget _renderItems(BuildContext context, List<View> views) {
+  Widget _renderSectionItems(BuildContext context, List<View> views) {
     var viewWidgets = views.map((view) {
-      final viewCtx = ViewWidgetContext(view);
-
       final item = ViewSectionItem(
-        viewCtx: viewCtx,
+        view: view,
         isSelected: _isViewSelected(context, view.id),
-        onOpen: (view) {
-          Log.debug("Open: $view");
-          context.read<ViewSectionNotifier>().setSelectedView(view);
-          final stackView = stackCtxFromView(viewCtx.view);
-          getIt<HomeStackManager>().setStack(stackView);
-        },
+        onSelected: (view) => context.read<ViewSectionNotifier>().setSelectedView(view),
       );
 
       return Padding(
@@ -83,17 +74,14 @@ class ViewSection extends StatelessWidget {
       );
     }).toList(growable: false);
 
-    return Column(
-      children: viewWidgets,
-    );
+    return Column(children: viewWidgets);
   }
 
   bool _isViewSelected(BuildContext context, String viewId) {
     final view = context.read<ViewSectionNotifier>().selectedView;
-    if (view != null) {
-      return view.id == viewId;
-    } else {
+    if (view == null) {
       return false;
     }
+    return view.id == viewId;
   }
 }