Przeglądaj źródła

[client]: close opened doc after close or delete the view

appflowy 3 lat temu
rodzic
commit
22cc76163b
44 zmienionych plików z 362 dodań i 893 usunięć
  1. 4 8
      app_flowy/lib/workspace/infrastructure/repos/doc_repo.dart
  2. 2 3
      app_flowy/lib/workspace/infrastructure/repos/view_repo.dart
  3. 19 2
      app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart
  4. 3 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart
  5. 3 2
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart
  6. 0 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/protobuf.dart
  7. 0 93
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_delete.pb.dart
  8. 0 7
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_delete.pbenum.dart
  9. 0 30
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_delete.pbjson.dart
  10. 0 9
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_delete.pbserver.dart
  11. 23 35
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pb.dart
  12. 8 8
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pbjson.dart
  13. 0 1
      backend/src/service/app/app.rs
  14. 2 2
      backend/src/service/view/router.rs
  15. 2 2
      backend/tests/api/doc.rs
  16. 2 2
      backend/tests/api/workspace.rs
  17. 1 1
      backend/tests/helper.rs
  18. 1 3
      rust-lib/flowy-derive/src/derive_cache/derive_cache.rs
  19. 8 0
      rust-lib/flowy-document/src/entities/doc/doc.rs
  20. 6 1
      rust-lib/flowy-document/src/module.rs
  21. 1 1
      rust-lib/flowy-document/src/services/doc/doc_controller.rs
  22. 8 5
      rust-lib/flowy-test/src/workspace.rs
  23. 0 1
      rust-lib/flowy-user/tests/event/auth_test.rs
  24. 0 2
      rust-lib/flowy-workspace/src/entities/view/mod.rs
  25. 0 33
      rust-lib/flowy-workspace/src/entities/view/view_delete.rs
  26. 23 27
      rust-lib/flowy-workspace/src/entities/view/view_query.rs
  27. 1 1
      rust-lib/flowy-workspace/src/entities/view/view_update.rs
  28. 1 1
      rust-lib/flowy-workspace/src/errors.rs
  29. 6 3
      rust-lib/flowy-workspace/src/event.rs
  30. 21 9
      rust-lib/flowy-workspace/src/handlers/view_handler.rs
  31. 1 0
      rust-lib/flowy-workspace/src/module.rs
  32. 60 54
      rust-lib/flowy-workspace/src/protobuf/model/event.rs
  33. 0 3
      rust-lib/flowy-workspace/src/protobuf/model/mod.rs
  34. 0 368
      rust-lib/flowy-workspace/src/protobuf/model/view_delete.rs
  35. 83 83
      rust-lib/flowy-workspace/src/protobuf/model/view_query.rs
  36. 2 1
      rust-lib/flowy-workspace/src/protobuf/proto/event.proto
  37. 0 8
      rust-lib/flowy-workspace/src/protobuf/proto/view_delete.proto
  38. 3 3
      rust-lib/flowy-workspace/src/protobuf/proto/view_query.proto
  39. 2 2
      rust-lib/flowy-workspace/src/services/server/mod.rs
  40. 3 3
      rust-lib/flowy-workspace/src/services/server/server_api.rs
  41. 2 2
      rust-lib/flowy-workspace/src/services/server/server_api_mock.rs
  42. 17 16
      rust-lib/flowy-workspace/src/services/trash_can.rs
  43. 9 4
      rust-lib/flowy-workspace/src/services/view_controller.rs
  44. 35 52
      rust-lib/flowy-workspace/tests/workspace/view_test.rs

+ 4 - 8
app_flowy/lib/workspace/infrastructure/repos/doc_repo.dart

@@ -1,5 +1,4 @@
 import 'package:dartz/dartz.dart';
-import 'package:flowy_log/flowy_log.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
@@ -12,7 +11,7 @@ class DocRepository {
   });
 
   Future<Either<DocDelta, WorkspaceError>> readDoc() {
-    final request = OpenViewRequest.create()..viewId = docId;
+    final request = QueryViewRequest(viewIds: [docId]);
     return WorkspaceEventOpenView(request).send();
   }
 
@@ -23,11 +22,8 @@ class DocRepository {
     return WorkspaceEventApplyDocDelta(request).send();
   }
 
-  Future<Either<Unit, WorkspaceError>> closeDoc({String? name, String? desc, String? text}) {
-    Log.error('Close the doc');
-
-    return Future(() {
-      return left(unit);
-    });
+  Future<Either<Unit, WorkspaceError>> closeDoc() {
+    final request = QueryViewRequest(viewIds: [docId]);
+    return WorkspaceEventCloseView(request).send();
   }
 }

+ 2 - 3
app_flowy/lib/workspace/infrastructure/repos/view_repo.dart

@@ -6,7 +6,6 @@ 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';
@@ -22,7 +21,7 @@ class ViewRepository {
   });
 
   Future<Either<View, WorkspaceError>> readView() {
-    final request = QueryViewRequest.create()..viewId = view.id;
+    final request = QueryViewRequest(viewIds: [view.id]);
     return WorkspaceEventReadView(request).send();
   }
 
@@ -41,7 +40,7 @@ class ViewRepository {
   }
 
   Future<Either<Unit, WorkspaceError>> delete() {
-    final request = DeleteViewRequest.create()..viewIds.add(view.id);
+    final request = QueryViewRequest.create()..viewIds.add(view.id);
     return WorkspaceEventDeleteView(request).send();
   }
 }

+ 19 - 2
app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart

@@ -221,7 +221,7 @@ class WorkspaceEventUpdateView {
 }
 
 class WorkspaceEventDeleteView {
-     DeleteViewRequest request;
+     QueryViewRequest request;
      WorkspaceEventDeleteView(this.request);
 
     Future<Either<Unit, WorkspaceError>> send() {
@@ -238,7 +238,7 @@ class WorkspaceEventDeleteView {
 }
 
 class WorkspaceEventOpenView {
-     OpenViewRequest request;
+     QueryViewRequest request;
      WorkspaceEventOpenView(this.request);
 
     Future<Either<DocDelta, WorkspaceError>> send() {
@@ -254,6 +254,23 @@ class WorkspaceEventOpenView {
     }
 }
 
+class WorkspaceEventCloseView {
+     QueryViewRequest request;
+     WorkspaceEventCloseView(this.request);
+
+    Future<Either<Unit, WorkspaceError>> send() {
+    final request = FFIRequest.create()
+          ..event = WorkspaceEvent.CloseView.toString()
+          ..payload = requestToBytes(this.request);
+
+    return Dispatch.asyncRequest(request)
+        .then((bytesResult) => bytesResult.fold(
+           (bytes) => left(unit),
+           (errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
+        ));
+    }
+}
+
 class WorkspaceEventApplyDocDelta {
      DocDelta request;
      WorkspaceEventApplyDocDelta(this.request);

+ 3 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart

@@ -25,7 +25,8 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
   static const WorkspaceEvent UpdateView = WorkspaceEvent._(203, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateView');
   static const WorkspaceEvent DeleteView = WorkspaceEvent._(204, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteView');
   static const WorkspaceEvent OpenView = WorkspaceEvent._(205, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenView');
-  static const WorkspaceEvent ApplyDocDelta = WorkspaceEvent._(206, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplyDocDelta');
+  static const WorkspaceEvent CloseView = WorkspaceEvent._(206, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CloseView');
+  static const WorkspaceEvent ApplyDocDelta = WorkspaceEvent._(207, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplyDocDelta');
   static const WorkspaceEvent ReadTrash = WorkspaceEvent._(300, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadTrash');
   static const WorkspaceEvent PutbackTrash = WorkspaceEvent._(301, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PutbackTrash');
   static const WorkspaceEvent DeleteTrash = WorkspaceEvent._(302, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteTrash');
@@ -49,6 +50,7 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
     UpdateView,
     DeleteView,
     OpenView,
+    CloseView,
     ApplyDocDelta,
     ReadTrash,
     PutbackTrash,

+ 3 - 2
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart

@@ -27,7 +27,8 @@ const WorkspaceEvent$json = const {
     const {'1': 'UpdateView', '2': 203},
     const {'1': 'DeleteView', '2': 204},
     const {'1': 'OpenView', '2': 205},
-    const {'1': 'ApplyDocDelta', '2': 206},
+    const {'1': 'CloseView', '2': 206},
+    const {'1': 'ApplyDocDelta', '2': 207},
     const {'1': 'ReadTrash', '2': 300},
     const {'1': 'PutbackTrash', '2': 301},
     const {'1': 'DeleteTrash', '2': 302},
@@ -38,4 +39,4 @@ const WorkspaceEvent$json = const {
 };
 
 /// Descriptor for `WorkspaceEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESDQoIT3BlblZpZXcQzQESEgoNQXBwbHlEb2NEZWx0YRDOARIOCglSZWFkVHJhc2gQrAISEQoMUHV0YmFja1RyYXNoEK0CEhAKC0RlbGV0ZVRyYXNoEK4CEg8KClJlc3RvcmVBbGwQrwISDgoJRGVsZXRlQWxsELACEhIKDUluaXRXb3Jrc3BhY2UQ6Ac=');
+final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESDQoIT3BlblZpZXcQzQESDgoJQ2xvc2VWaWV3EM4BEhIKDUFwcGx5RG9jRGVsdGEQzwESDgoJUmVhZFRyYXNoEKwCEhEKDFB1dGJhY2tUcmFzaBCtAhIQCgtEZWxldGVUcmFzaBCuAhIPCgpSZXN0b3JlQWxsEK8CEg4KCURlbGV0ZUFsbBCwAhISCg1Jbml0V29ya3NwYWNlEOgH');

+ 0 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/protobuf.dart

@@ -1,6 +1,5 @@
 // Auto-generated, do not edit 
 export './view_update.pb.dart';
-export './view_delete.pb.dart';
 export './app_query.pb.dart';
 export './workspace_delete.pb.dart';
 export './observable.pb.dart';

+ 0 - 93
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_delete.pb.dart

@@ -1,93 +0,0 @@
-///
-//  Generated code. Do not modify.
-//  source: view_delete.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
-
-import 'dart:core' as $core;
-
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class DeleteViewRequest extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DeleteViewRequest', createEmptyInstance: create)
-    ..pPS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewIds')
-    ..hasRequiredFields = false
-  ;
-
-  DeleteViewRequest._() : super();
-  factory DeleteViewRequest({
-    $core.Iterable<$core.String>? viewIds,
-  }) {
-    final _result = create();
-    if (viewIds != null) {
-      _result.viewIds.addAll(viewIds);
-    }
-    return _result;
-  }
-  factory DeleteViewRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory DeleteViewRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
-  'Will be removed in next major version')
-  DeleteViewRequest clone() => DeleteViewRequest()..mergeFromMessage(this);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
-  'Will be removed in next major version')
-  DeleteViewRequest copyWith(void Function(DeleteViewRequest) updates) => super.copyWith((message) => updates(message as DeleteViewRequest)) as DeleteViewRequest; // ignore: deprecated_member_use
-  $pb.BuilderInfo get info_ => _i;
-  @$core.pragma('dart2js:noInline')
-  static DeleteViewRequest create() => DeleteViewRequest._();
-  DeleteViewRequest createEmptyInstance() => create();
-  static $pb.PbList<DeleteViewRequest> createRepeated() => $pb.PbList<DeleteViewRequest>();
-  @$core.pragma('dart2js:noInline')
-  static DeleteViewRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DeleteViewRequest>(create);
-  static DeleteViewRequest? _defaultInstance;
-
-  @$pb.TagNumber(1)
-  $core.List<$core.String> get viewIds => $_getList(0);
-}
-
-class DeleteViewParams extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DeleteViewParams', createEmptyInstance: create)
-    ..pPS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewIds')
-    ..hasRequiredFields = false
-  ;
-
-  DeleteViewParams._() : super();
-  factory DeleteViewParams({
-    $core.Iterable<$core.String>? viewIds,
-  }) {
-    final _result = create();
-    if (viewIds != null) {
-      _result.viewIds.addAll(viewIds);
-    }
-    return _result;
-  }
-  factory DeleteViewParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory DeleteViewParams.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
-  'Will be removed in next major version')
-  DeleteViewParams clone() => DeleteViewParams()..mergeFromMessage(this);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
-  'Will be removed in next major version')
-  DeleteViewParams copyWith(void Function(DeleteViewParams) updates) => super.copyWith((message) => updates(message as DeleteViewParams)) as DeleteViewParams; // ignore: deprecated_member_use
-  $pb.BuilderInfo get info_ => _i;
-  @$core.pragma('dart2js:noInline')
-  static DeleteViewParams create() => DeleteViewParams._();
-  DeleteViewParams createEmptyInstance() => create();
-  static $pb.PbList<DeleteViewParams> createRepeated() => $pb.PbList<DeleteViewParams>();
-  @$core.pragma('dart2js:noInline')
-  static DeleteViewParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DeleteViewParams>(create);
-  static DeleteViewParams? _defaultInstance;
-
-  @$pb.TagNumber(1)
-  $core.List<$core.String> get viewIds => $_getList(0);
-}
-

+ 0 - 7
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_delete.pbenum.dart

@@ -1,7 +0,0 @@
-///
-//  Generated code. Do not modify.
-//  source: view_delete.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
-

+ 0 - 30
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_delete.pbjson.dart

@@ -1,30 +0,0 @@
-///
-//  Generated code. Do not modify.
-//  source: view_delete.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-@$core.Deprecated('Use deleteViewRequestDescriptor instead')
-const DeleteViewRequest$json = const {
-  '1': 'DeleteViewRequest',
-  '2': const [
-    const {'1': 'view_ids', '3': 1, '4': 3, '5': 9, '10': 'viewIds'},
-  ],
-};
-
-/// Descriptor for `DeleteViewRequest`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List deleteViewRequestDescriptor = $convert.base64Decode('ChFEZWxldGVWaWV3UmVxdWVzdBIZCgh2aWV3X2lkcxgBIAMoCVIHdmlld0lkcw==');
-@$core.Deprecated('Use deleteViewParamsDescriptor instead')
-const DeleteViewParams$json = const {
-  '1': 'DeleteViewParams',
-  '2': const [
-    const {'1': 'view_ids', '3': 1, '4': 3, '5': 9, '10': 'viewIds'},
-  ],
-};
-
-/// Descriptor for `DeleteViewParams`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List deleteViewParamsDescriptor = $convert.base64Decode('ChBEZWxldGVWaWV3UGFyYW1zEhkKCHZpZXdfaWRzGAEgAygJUgd2aWV3SWRz');

+ 0 - 9
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_delete.pbserver.dart

@@ -1,9 +0,0 @@
-///
-//  Generated code. Do not modify.
-//  source: view_delete.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
-
-export 'view_delete.pb.dart';
-

+ 23 - 35
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pb.dart

@@ -11,17 +11,17 @@ import 'package:protobuf/protobuf.dart' as $pb;
 
 class QueryViewRequest extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'QueryViewRequest', createEmptyInstance: create)
-    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
+    ..pPS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewIds')
     ..hasRequiredFields = false
   ;
 
   QueryViewRequest._() : super();
   factory QueryViewRequest({
-    $core.String? viewId,
+    $core.Iterable<$core.String>? viewIds,
   }) {
     final _result = create();
-    if (viewId != null) {
-      _result.viewId = viewId;
+    if (viewIds != null) {
+      _result.viewIds.addAll(viewIds);
     }
     return _result;
   }
@@ -47,13 +47,7 @@ class QueryViewRequest extends $pb.GeneratedMessage {
   static QueryViewRequest? _defaultInstance;
 
   @$pb.TagNumber(1)
-  $core.String get viewId => $_getSZ(0);
-  @$pb.TagNumber(1)
-  set viewId($core.String v) { $_setString(0, v); }
-  @$pb.TagNumber(1)
-  $core.bool hasViewId() => $_has(0);
-  @$pb.TagNumber(1)
-  void clearViewId() => clearField(1);
+  $core.List<$core.String> get viewIds => $_getList(0);
 }
 
 class ViewIdentifier extends $pb.GeneratedMessage {
@@ -103,50 +97,44 @@ class ViewIdentifier extends $pb.GeneratedMessage {
   void clearViewId() => clearField(1);
 }
 
-class OpenViewRequest extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'OpenViewRequest', createEmptyInstance: create)
-    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
+class ViewIdentifiers extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ViewIdentifiers', createEmptyInstance: create)
+    ..pPS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewIds')
     ..hasRequiredFields = false
   ;
 
-  OpenViewRequest._() : super();
-  factory OpenViewRequest({
-    $core.String? viewId,
+  ViewIdentifiers._() : super();
+  factory ViewIdentifiers({
+    $core.Iterable<$core.String>? viewIds,
   }) {
     final _result = create();
-    if (viewId != null) {
-      _result.viewId = viewId;
+    if (viewIds != null) {
+      _result.viewIds.addAll(viewIds);
     }
     return _result;
   }
-  factory OpenViewRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory OpenViewRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory ViewIdentifiers.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory ViewIdentifiers.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
   'Will be removed in next major version')
-  OpenViewRequest clone() => OpenViewRequest()..mergeFromMessage(this);
+  ViewIdentifiers clone() => ViewIdentifiers()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  OpenViewRequest copyWith(void Function(OpenViewRequest) updates) => super.copyWith((message) => updates(message as OpenViewRequest)) as OpenViewRequest; // ignore: deprecated_member_use
+  ViewIdentifiers copyWith(void Function(ViewIdentifiers) updates) => super.copyWith((message) => updates(message as ViewIdentifiers)) as ViewIdentifiers; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static OpenViewRequest create() => OpenViewRequest._();
-  OpenViewRequest createEmptyInstance() => create();
-  static $pb.PbList<OpenViewRequest> createRepeated() => $pb.PbList<OpenViewRequest>();
+  static ViewIdentifiers create() => ViewIdentifiers._();
+  ViewIdentifiers createEmptyInstance() => create();
+  static $pb.PbList<ViewIdentifiers> createRepeated() => $pb.PbList<ViewIdentifiers>();
   @$core.pragma('dart2js:noInline')
-  static OpenViewRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<OpenViewRequest>(create);
-  static OpenViewRequest? _defaultInstance;
+  static ViewIdentifiers getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ViewIdentifiers>(create);
+  static ViewIdentifiers? _defaultInstance;
 
   @$pb.TagNumber(1)
-  $core.String get viewId => $_getSZ(0);
-  @$pb.TagNumber(1)
-  set viewId($core.String v) { $_setString(0, v); }
-  @$pb.TagNumber(1)
-  $core.bool hasViewId() => $_has(0);
-  @$pb.TagNumber(1)
-  void clearViewId() => clearField(1);
+  $core.List<$core.String> get viewIds => $_getList(0);
 }
 

+ 8 - 8
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pbjson.dart

@@ -12,12 +12,12 @@ import 'dart:typed_data' as $typed_data;
 const QueryViewRequest$json = const {
   '1': 'QueryViewRequest',
   '2': const [
-    const {'1': 'view_id', '3': 1, '4': 1, '5': 9, '10': 'viewId'},
+    const {'1': 'view_ids', '3': 1, '4': 3, '5': 9, '10': 'viewIds'},
   ],
 };
 
 /// Descriptor for `QueryViewRequest`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List queryViewRequestDescriptor = $convert.base64Decode('ChBRdWVyeVZpZXdSZXF1ZXN0EhcKB3ZpZXdfaWQYASABKAlSBnZpZXdJZA==');
+final $typed_data.Uint8List queryViewRequestDescriptor = $convert.base64Decode('ChBRdWVyeVZpZXdSZXF1ZXN0EhkKCHZpZXdfaWRzGAEgAygJUgd2aWV3SWRz');
 @$core.Deprecated('Use viewIdentifierDescriptor instead')
 const ViewIdentifier$json = const {
   '1': 'ViewIdentifier',
@@ -28,13 +28,13 @@ const ViewIdentifier$json = const {
 
 /// Descriptor for `ViewIdentifier`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List viewIdentifierDescriptor = $convert.base64Decode('Cg5WaWV3SWRlbnRpZmllchIXCgd2aWV3X2lkGAEgASgJUgZ2aWV3SWQ=');
-@$core.Deprecated('Use openViewRequestDescriptor instead')
-const OpenViewRequest$json = const {
-  '1': 'OpenViewRequest',
+@$core.Deprecated('Use viewIdentifiersDescriptor instead')
+const ViewIdentifiers$json = const {
+  '1': 'ViewIdentifiers',
   '2': const [
-    const {'1': 'view_id', '3': 1, '4': 1, '5': 9, '10': 'viewId'},
+    const {'1': 'view_ids', '3': 1, '4': 3, '5': 9, '10': 'viewIds'},
   ],
 };
 
-/// Descriptor for `OpenViewRequest`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List openViewRequestDescriptor = $convert.base64Decode('Cg9PcGVuVmlld1JlcXVlc3QSFwoHdmlld19pZBgBIAEoCVIGdmlld0lk');
+/// Descriptor for `ViewIdentifiers`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List viewIdentifiersDescriptor = $convert.base64Decode('Cg9WaWV3SWRlbnRpZmllcnMSGQoIdmlld19pZHMYASADKAlSB3ZpZXdJZHM=');

+ 0 - 1
backend/src/service/app/app.rs

@@ -13,7 +13,6 @@ use flowy_workspace::{
     },
     protobuf::{App, CreateAppParams, RepeatedView},
 };
-use protobuf::Message;
 use sqlx::{postgres::PgArguments, Postgres};
 use uuid::Uuid;
 

+ 2 - 2
backend/src/service/view/router.rs

@@ -15,7 +15,7 @@ use flowy_net::{
 };
 use flowy_workspace::{
     entities::view::parser::{ViewDesc, ViewName, ViewThumbnail},
-    protobuf::{CreateViewParams, DeleteViewParams, UpdateViewParams, ViewIdentifier},
+    protobuf::{CreateViewParams, QueryViewRequest, UpdateViewParams, ViewIdentifier},
 };
 use sqlx::PgPool;
 use std::sync::Arc;
@@ -96,7 +96,7 @@ pub async fn update_handler(payload: Payload, pool: Data<PgPool>) -> Result<Http
 }
 
 pub async fn delete_handler(payload: Payload, pool: Data<PgPool>) -> Result<HttpResponse, ServerError> {
-    let params: DeleteViewParams = parse_from_payload(payload).await?;
+    let params: QueryViewRequest = parse_from_payload(payload).await?;
     let view_ids = check_view_ids(params.view_ids.to_vec())?;
     let mut transaction = pool
         .begin()

+ 2 - 2
backend/tests/api/doc.rs

@@ -1,6 +1,6 @@
 use crate::helper::ViewTest;
 use flowy_document::entities::doc::DocIdentifier;
-use flowy_workspace::entities::view::DeleteViewParams;
+use flowy_workspace::entities::view::ViewIdentifiers;
 
 #[actix_rt::test]
 async fn doc_read() {
@@ -17,7 +17,7 @@ async fn doc_read() {
 #[actix_rt::test]
 async fn doc_delete() {
     let test = ViewTest::new().await;
-    let delete_params = DeleteViewParams {
+    let delete_params = ViewIdentifiers {
         view_ids: vec![test.view.id.clone()],
     };
     test.server.delete_view(delete_params).await;

+ 2 - 2
backend/tests/api/workspace.rs

@@ -147,7 +147,7 @@ async fn view_update() {
     test.server.update_view(update_params).await;
 
     // read
-    let read_params = ViewIdentifier::new(&test.view.id);
+    let read_params: ViewIdentifier = test.view.id.clone().into();
     let view = test.server.read_view(read_params).await.unwrap();
     assert_eq!(&view.name, new_name);
 }
@@ -166,7 +166,7 @@ async fn view_delete() {
         .map(|item| item.id)
         .collect::<Vec<String>>();
     // read
-    let read_params = ViewIdentifier::new(&test.view.id);
+    let read_params: ViewIdentifier = test.view.id.clone().into();
 
     // the view can't read from the server. it should be in the trash
     assert_eq!(test.server.read_view(read_params).await.is_none(), true);

+ 1 - 1
backend/tests/helper.rs

@@ -117,7 +117,7 @@ impl TestUserServer {
         update_view_request(self.user_token(), params, &url).await.unwrap();
     }
 
-    pub async fn delete_view(&self, params: DeleteViewParams) {
+    pub async fn delete_view(&self, params: ViewIdentifiers) {
         let url = format!("{}/api/view", self.http_addr());
         delete_view_request(self.user_token(), params, &url).await.unwrap();
     }

+ 1 - 3
rust-lib/flowy-derive/src/derive_cache/derive_cache.rs

@@ -44,11 +44,9 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         | "RepeatedTrash"
         | "UpdateViewRequest"
         | "UpdateViewParams"
-        | "DeleteViewRequest"
-        | "DeleteViewParams"
         | "QueryViewRequest"
         | "ViewIdentifier"
-        | "OpenViewRequest"
+        | "ViewIdentifiers"
         | "CreateViewRequest"
         | "CreateViewParams"
         | "View"

+ 8 - 0
rust-lib/flowy-document/src/entities/doc/doc.rs

@@ -84,3 +84,11 @@ pub struct DocIdentifier {
 impl std::convert::From<String> for DocIdentifier {
     fn from(doc_id: String) -> Self { DocIdentifier { doc_id } }
 }
+
+impl std::convert::From<&String> for DocIdentifier {
+    fn from(doc_id: &String) -> Self {
+        DocIdentifier {
+            doc_id: doc_id.to_owned(),
+        }
+    }
+}

+ 6 - 1
rust-lib/flowy-document/src/module.rs

@@ -1,5 +1,5 @@
 use crate::{
-    entities::doc::{CreateDocParams, DocDelta, DocIdentifier},
+    entities::doc::{DocDelta, DocIdentifier},
     errors::DocError,
     services::{
         doc::{doc_controller::DocController, edit::ClientEditDoc},
@@ -47,6 +47,11 @@ impl FlowyDocument {
         Ok(edit_context)
     }
 
+    pub async fn close(&self, params: DocIdentifier) -> Result<(), DocError> {
+        let _ = self.doc_ctrl.close(&params.doc_id)?;
+        Ok(())
+    }
+
     pub async fn apply_doc_delta(&self, params: DocDelta) -> Result<DocDelta, DocError> {
         // workaround: compare the rust's delta with flutter's delta. Will be removed
         // very soon

+ 1 - 1
rust-lib/flowy-document/src/services/doc/doc_controller.rs

@@ -3,7 +3,7 @@ use std::sync::Arc;
 use bytes::Bytes;
 
 use crate::{
-    entities::doc::{CreateDocParams, Doc, DocDelta, DocIdentifier},
+    entities::doc::{Doc, DocDelta, DocIdentifier},
     errors::{DocError, DocResult},
     module::DocumentUser,
     services::{

+ 8 - 5
rust-lib/flowy-test/src/workspace.rs

@@ -82,12 +82,15 @@ impl ViewTest {
         }
     }
 
-    pub async fn delete_view(&self, view_ids: Vec<String>) {
-        let request = DeleteViewRequest { view_ids };
+    pub async fn delete_views(&self, view_ids: Vec<String>) {
+        let request = QueryViewRequest { view_ids };
         delete_view(&self.sdk, request).await;
     }
 
-    pub async fn delete_all_view(&self) {
+    pub async fn delete_views_permanent(&self, view_ids: Vec<String>) {
+        let request = QueryViewRequest { view_ids };
+        delete_view(&self.sdk, request).await;
+
         FlowyWorkspaceTest::new(self.sdk.clone())
             .event(DeleteAll)
             .async_send()
@@ -238,7 +241,7 @@ pub async fn read_view(sdk: &FlowyTestSDK, request: QueryViewRequest) -> View {
         .parse::<View>()
 }
 
-pub async fn delete_view(sdk: &FlowyTestSDK, request: DeleteViewRequest) {
+pub async fn delete_view(sdk: &FlowyTestSDK, request: QueryViewRequest) {
     FlowyWorkspaceTest::new(sdk.clone())
         .event(DeleteView)
         .request(request)
@@ -262,7 +265,7 @@ pub async fn putback_trash(sdk: &FlowyTestSDK, id: TrashIdentifier) {
         .await;
 }
 
-pub async fn open_view(sdk: &FlowyTestSDK, request: OpenViewRequest) -> Doc {
+pub async fn open_view(sdk: &FlowyTestSDK, request: QueryViewRequest) -> Doc {
     FlowyWorkspaceTest::new(sdk.clone())
         .event(OpenView)
         .request(request)

+ 0 - 1
rust-lib/flowy-user/tests/event/auth_test.rs

@@ -1,7 +1,6 @@
 use crate::helper::*;
 use flowy_test::{builder::UserTest, FlowyTest};
 use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
-use serial_test::*;
 
 #[tokio::test]
 async fn sign_up_with_invalid_email() {

+ 0 - 2
rust-lib/flowy-workspace/src/entities/view/mod.rs

@@ -1,10 +1,8 @@
 pub mod parser;
 mod view_create;
-mod view_delete;
 mod view_query;
 mod view_update;
 
 pub use view_create::*;
-pub use view_delete::*;
 pub use view_query::*;
 pub use view_update::*;

+ 0 - 33
rust-lib/flowy-workspace/src/entities/view/view_delete.rs

@@ -1,33 +0,0 @@
-use crate::{entities::view::parser::ViewId, errors::WorkspaceError};
-use flowy_derive::ProtoBuf;
-
-use std::convert::TryInto;
-
-#[derive(Default, ProtoBuf)]
-pub struct DeleteViewRequest {
-    #[pb(index = 1)]
-    pub view_ids: Vec<String>,
-}
-
-#[derive(Default, ProtoBuf)]
-pub struct DeleteViewParams {
-    #[pb(index = 1)]
-    pub view_ids: Vec<String>,
-}
-
-impl TryInto<DeleteViewParams> for DeleteViewRequest {
-    type Error = WorkspaceError;
-
-    fn try_into(self) -> Result<DeleteViewParams, Self::Error> {
-        let mut view_ids = vec![];
-        for view_id in self.view_ids {
-            let view_id = ViewId::parse(view_id)
-                .map_err(|e| WorkspaceError::view_id().context(e))?
-                .0;
-
-            view_ids.push(view_id);
-        }
-
-        Ok(DeleteViewParams { view_ids })
-    }
-}

+ 23 - 27
rust-lib/flowy-workspace/src/entities/view/view_query.rs

@@ -6,15 +6,7 @@ use std::convert::TryInto;
 #[derive(Default, ProtoBuf)]
 pub struct QueryViewRequest {
     #[pb(index = 1)]
-    pub view_id: String,
-}
-
-impl QueryViewRequest {
-    pub fn new(view_id: &str) -> Self {
-        Self {
-            view_id: view_id.to_owned(),
-        }
-    }
+    pub view_ids: Vec<String>,
 }
 
 #[derive(Default, ProtoBuf, Clone, Debug)]
@@ -23,14 +15,6 @@ pub struct ViewIdentifier {
     pub view_id: String,
 }
 
-impl ViewIdentifier {
-    pub fn new(view_id: &str) -> Self {
-        Self {
-            view_id: view_id.to_owned(),
-        }
-    }
-}
-
 impl std::convert::From<String> for ViewIdentifier {
     fn from(view_id: String) -> Self { ViewIdentifier { view_id } }
 }
@@ -42,8 +26,14 @@ impl std::convert::Into<DocIdentifier> for ViewIdentifier {
 impl TryInto<ViewIdentifier> for QueryViewRequest {
     type Error = WorkspaceError;
     fn try_into(self) -> Result<ViewIdentifier, Self::Error> {
-        let view_id = ViewId::parse(self.view_id)
-            .map_err(|e| WorkspaceError::view_id().context(e))?
+        debug_assert!(self.view_ids.len() == 1);
+        if self.view_ids.len() != 1 {
+            return Err(WorkspaceError::invalid_view_id().context("The len of view_ids should be equal to 1"));
+        }
+
+        let view_id = self.view_ids.first().unwrap().clone();
+        let view_id = ViewId::parse(view_id)
+            .map_err(|e| WorkspaceError::invalid_view_id().context(e))?
             .0;
 
         Ok(ViewIdentifier { view_id })
@@ -51,18 +41,24 @@ impl TryInto<ViewIdentifier> for QueryViewRequest {
 }
 
 #[derive(Default, ProtoBuf)]
-pub struct OpenViewRequest {
+pub struct ViewIdentifiers {
     #[pb(index = 1)]
-    pub view_id: String,
+    pub view_ids: Vec<String>,
 }
 
-impl std::convert::TryInto<DocIdentifier> for OpenViewRequest {
+impl TryInto<ViewIdentifiers> for QueryViewRequest {
     type Error = WorkspaceError;
 
-    fn try_into(self) -> Result<DocIdentifier, Self::Error> {
-        let view_id = ViewId::parse(self.view_id)
-            .map_err(|e| WorkspaceError::view_id().context(e))?
-            .0;
-        Ok(DocIdentifier { doc_id: view_id })
+    fn try_into(self) -> Result<ViewIdentifiers, Self::Error> {
+        let mut view_ids = vec![];
+        for view_id in self.view_ids {
+            let view_id = ViewId::parse(view_id)
+                .map_err(|e| WorkspaceError::invalid_view_id().context(e))?
+                .0;
+
+            view_ids.push(view_id);
+        }
+
+        Ok(ViewIdentifiers { view_ids })
     }
 }

+ 1 - 1
rust-lib/flowy-workspace/src/entities/view/view_update.rs

@@ -60,7 +60,7 @@ impl TryInto<UpdateViewParams> for UpdateViewRequest {
 
     fn try_into(self) -> Result<UpdateViewParams, Self::Error> {
         let view_id = ViewId::parse(self.view_id)
-            .map_err(|e| WorkspaceError::view_id().context(e))?
+            .map_err(|e| WorkspaceError::invalid_view_id().context(e))?
             .0;
 
         let name = match self.name {

+ 1 - 1
rust-lib/flowy-workspace/src/errors.rs

@@ -45,7 +45,7 @@ impl WorkspaceError {
     static_workspace_error!(app_id, ErrorCode::AppIdInvalid);
     static_workspace_error!(view_name, ErrorCode::ViewNameInvalid);
     static_workspace_error!(view_thumbnail, ErrorCode::ViewThumbnailInvalid);
-    static_workspace_error!(view_id, ErrorCode::ViewIdInvalid);
+    static_workspace_error!(invalid_view_id, ErrorCode::ViewIdInvalid);
     static_workspace_error!(view_desc, ErrorCode::ViewDescInvalid);
     static_workspace_error!(view_data, ErrorCode::ViewDataInvalid);
     static_workspace_error!(unauthorized, ErrorCode::UserUnauthorized);

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

@@ -43,14 +43,17 @@ pub enum WorkspaceEvent {
     #[event(input = "UpdateViewRequest", output = "View")]
     UpdateView        = 203,
 
-    #[event(input = "DeleteViewRequest")]
+    #[event(input = "QueryViewRequest")]
     DeleteView        = 204,
 
-    #[event(input = "OpenViewRequest", output = "DocDelta")]
+    #[event(input = "QueryViewRequest", output = "DocDelta")]
     OpenView          = 205,
 
+    #[event(input = "QueryViewRequest")]
+    CloseView         = 206,
+
     #[event(input = "DocDelta", output = "DocDelta")]
-    ApplyDocDelta     = 206,
+    ApplyDocDelta     = 207,
 
     #[event(output = "RepeatedTrash")]
     ReadTrash         = 300,

+ 21 - 9
rust-lib/flowy-workspace/src/handlers/view_handler.rs

@@ -4,21 +4,19 @@ use crate::{
         view::{
             CreateViewParams,
             CreateViewRequest,
-            DeleteViewParams,
-            DeleteViewRequest,
-            OpenViewRequest,
             QueryViewRequest,
             UpdateViewParams,
             UpdateViewRequest,
             View,
             ViewIdentifier,
+            ViewIdentifiers,
         },
     },
     errors::WorkspaceError,
     services::{TrashCan, ViewController},
 };
 use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit};
-use flowy_document::entities::doc::{DocDelta, DocIdentifier};
+use flowy_document::entities::doc::DocDelta;
 use std::{convert::TryInto, sync::Arc};
 
 #[tracing::instrument(skip(data, controller), err)]
@@ -66,11 +64,15 @@ pub(crate) async fn apply_doc_delta_handler(
 
 #[tracing::instrument(skip(data, controller, trash_can), err)]
 pub(crate) async fn delete_view_handler(
-    data: Data<DeleteViewRequest>,
+    data: Data<QueryViewRequest>,
     controller: Unit<Arc<ViewController>>,
     trash_can: Unit<Arc<TrashCan>>,
 ) -> Result<(), WorkspaceError> {
-    let params: DeleteViewParams = data.into_inner().try_into()?;
+    let params: ViewIdentifiers = data.into_inner().try_into()?;
+    for view_id in &params.view_ids {
+        let _ = controller.close_view(view_id.into()).await;
+    }
+
     let trash = controller
         .read_view_tables(params.view_ids)?
         .into_iter()
@@ -83,10 +85,20 @@ pub(crate) async fn delete_view_handler(
 
 #[tracing::instrument(skip(data, controller), err)]
 pub(crate) async fn open_view_handler(
-    data: Data<OpenViewRequest>,
+    data: Data<QueryViewRequest>,
     controller: Unit<Arc<ViewController>>,
 ) -> DataResult<DocDelta, WorkspaceError> {
-    let params: DocIdentifier = data.into_inner().try_into()?;
-    let doc = controller.open_view(params).await?;
+    let params: ViewIdentifier = data.into_inner().try_into()?;
+    let doc = controller.open_view(params.into()).await?;
     data_result(doc)
 }
+
+#[tracing::instrument(skip(data, controller), err)]
+pub(crate) async fn close_view_handler(
+    data: Data<QueryViewRequest>,
+    controller: Unit<Arc<ViewController>>,
+) -> Result<(), WorkspaceError> {
+    let params: ViewIdentifier = data.into_inner().try_into()?;
+    let _ = controller.close_view(params.into()).await?;
+    Ok(())
+}

+ 1 - 0
rust-lib/flowy-workspace/src/module.rs

@@ -89,6 +89,7 @@ pub fn create(workspace: Arc<WorkspaceController>) -> Module {
         .event(WorkspaceEvent::UpdateView, update_view_handler)
         .event(WorkspaceEvent::DeleteView, delete_view_handler)
         .event(WorkspaceEvent::OpenView, open_view_handler)
+        .event(WorkspaceEvent::CloseView, close_view_handler)
         .event(WorkspaceEvent::ApplyDocDelta, apply_doc_delta_handler);
 
     module = module

+ 60 - 54
rust-lib/flowy-workspace/src/protobuf/model/event.rs

@@ -40,7 +40,8 @@ pub enum WorkspaceEvent {
     UpdateView = 203,
     DeleteView = 204,
     OpenView = 205,
-    ApplyDocDelta = 206,
+    CloseView = 206,
+    ApplyDocDelta = 207,
     ReadTrash = 300,
     PutbackTrash = 301,
     DeleteTrash = 302,
@@ -71,7 +72,8 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
             203 => ::std::option::Option::Some(WorkspaceEvent::UpdateView),
             204 => ::std::option::Option::Some(WorkspaceEvent::DeleteView),
             205 => ::std::option::Option::Some(WorkspaceEvent::OpenView),
-            206 => ::std::option::Option::Some(WorkspaceEvent::ApplyDocDelta),
+            206 => ::std::option::Option::Some(WorkspaceEvent::CloseView),
+            207 => ::std::option::Option::Some(WorkspaceEvent::ApplyDocDelta),
             300 => ::std::option::Option::Some(WorkspaceEvent::ReadTrash),
             301 => ::std::option::Option::Some(WorkspaceEvent::PutbackTrash),
             302 => ::std::option::Option::Some(WorkspaceEvent::DeleteTrash),
@@ -99,6 +101,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
             WorkspaceEvent::UpdateView,
             WorkspaceEvent::DeleteView,
             WorkspaceEvent::OpenView,
+            WorkspaceEvent::CloseView,
             WorkspaceEvent::ApplyDocDelta,
             WorkspaceEvent::ReadTrash,
             WorkspaceEvent::PutbackTrash,
@@ -134,64 +137,67 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0bevent.proto*\x97\x03\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\
+    \n\x0bevent.proto*\xa7\x03\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\
     ace\x10\0\x12\x14\n\x10ReadCurWorkspace\x10\x01\x12\x12\n\x0eReadWorkspa\
     ces\x10\x02\x12\x13\n\x0fDeleteWorkspace\x10\x03\x12\x11\n\rOpenWorkspac\
     e\x10\x04\x12\x15\n\x11ReadWorkspaceApps\x10\x05\x12\r\n\tCreateApp\x10e\
     \x12\r\n\tDeleteApp\x10f\x12\x0b\n\x07ReadApp\x10g\x12\r\n\tUpdateApp\
     \x10h\x12\x0f\n\nCreateView\x10\xc9\x01\x12\r\n\x08ReadView\x10\xca\x01\
     \x12\x0f\n\nUpdateView\x10\xcb\x01\x12\x0f\n\nDeleteView\x10\xcc\x01\x12\
-    \r\n\x08OpenView\x10\xcd\x01\x12\x12\n\rApplyDocDelta\x10\xce\x01\x12\
-    \x0e\n\tReadTrash\x10\xac\x02\x12\x11\n\x0cPutbackTrash\x10\xad\x02\x12\
-    \x10\n\x0bDeleteTrash\x10\xae\x02\x12\x0f\n\nRestoreAll\x10\xaf\x02\x12\
-    \x0e\n\tDeleteAll\x10\xb0\x02\x12\x12\n\rInitWorkspace\x10\xe8\x07J\xb0\
-    \x07\n\x06\x12\x04\0\0\x19\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
-    \x05\0\x12\x04\x02\0\x19\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x13\n\
-    \x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x18\n\x0c\n\x05\x05\0\x02\0\x01\
-    \x12\x03\x03\x04\x13\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\x16\x17\n\
-    \x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x19\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\
-    \x18\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\x17\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\x16\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\x04\x18\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\x17\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x07\x04\x16\n\x0c\n\x05\
-    \x05\0\x02\x04\x01\x12\x03\x07\x04\x11\n\x0c\n\x05\x05\0\x02\x04\x02\x12\
-    \x03\x07\x14\x15\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x08\x04\x1a\n\x0c\n\
-    \x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x15\n\x0c\n\x05\x05\0\x02\x05\x02\
-    \x12\x03\x08\x18\x19\n\x0b\n\x04\x05\0\x02\x06\x12\x03\t\x04\x14\n\x0c\n\
-    \x05\x05\0\x02\x06\x01\x12\x03\t\x04\r\n\x0c\n\x05\x05\0\x02\x06\x02\x12\
-    \x03\t\x10\x13\n\x0b\n\x04\x05\0\x02\x07\x12\x03\n\x04\x14\n\x0c\n\x05\
-    \x05\0\x02\x07\x01\x12\x03\n\x04\r\n\x0c\n\x05\x05\0\x02\x07\x02\x12\x03\
-    \n\x10\x13\n\x0b\n\x04\x05\0\x02\x08\x12\x03\x0b\x04\x12\n\x0c\n\x05\x05\
-    \0\x02\x08\x01\x12\x03\x0b\x04\x0b\n\x0c\n\x05\x05\0\x02\x08\x02\x12\x03\
-    \x0b\x0e\x11\n\x0b\n\x04\x05\0\x02\t\x12\x03\x0c\x04\x14\n\x0c\n\x05\x05\
-    \0\x02\t\x01\x12\x03\x0c\x04\r\n\x0c\n\x05\x05\0\x02\t\x02\x12\x03\x0c\
-    \x10\x13\n\x0b\n\x04\x05\0\x02\n\x12\x03\r\x04\x15\n\x0c\n\x05\x05\0\x02\
-    \n\x01\x12\x03\r\x04\x0e\n\x0c\n\x05\x05\0\x02\n\x02\x12\x03\r\x11\x14\n\
-    \x0b\n\x04\x05\0\x02\x0b\x12\x03\x0e\x04\x13\n\x0c\n\x05\x05\0\x02\x0b\
-    \x01\x12\x03\x0e\x04\x0c\n\x0c\n\x05\x05\0\x02\x0b\x02\x12\x03\x0e\x0f\
-    \x12\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\x0f\x04\x15\n\x0c\n\x05\x05\0\x02\
-    \x0c\x01\x12\x03\x0f\x04\x0e\n\x0c\n\x05\x05\0\x02\x0c\x02\x12\x03\x0f\
-    \x11\x14\n\x0b\n\x04\x05\0\x02\r\x12\x03\x10\x04\x15\n\x0c\n\x05\x05\0\
-    \x02\r\x01\x12\x03\x10\x04\x0e\n\x0c\n\x05\x05\0\x02\r\x02\x12\x03\x10\
-    \x11\x14\n\x0b\n\x04\x05\0\x02\x0e\x12\x03\x11\x04\x13\n\x0c\n\x05\x05\0\
-    \x02\x0e\x01\x12\x03\x11\x04\x0c\n\x0c\n\x05\x05\0\x02\x0e\x02\x12\x03\
-    \x11\x0f\x12\n\x0b\n\x04\x05\0\x02\x0f\x12\x03\x12\x04\x18\n\x0c\n\x05\
-    \x05\0\x02\x0f\x01\x12\x03\x12\x04\x11\n\x0c\n\x05\x05\0\x02\x0f\x02\x12\
-    \x03\x12\x14\x17\n\x0b\n\x04\x05\0\x02\x10\x12\x03\x13\x04\x14\n\x0c\n\
-    \x05\x05\0\x02\x10\x01\x12\x03\x13\x04\r\n\x0c\n\x05\x05\0\x02\x10\x02\
-    \x12\x03\x13\x10\x13\n\x0b\n\x04\x05\0\x02\x11\x12\x03\x14\x04\x17\n\x0c\
-    \n\x05\x05\0\x02\x11\x01\x12\x03\x14\x04\x10\n\x0c\n\x05\x05\0\x02\x11\
-    \x02\x12\x03\x14\x13\x16\n\x0b\n\x04\x05\0\x02\x12\x12\x03\x15\x04\x16\n\
-    \x0c\n\x05\x05\0\x02\x12\x01\x12\x03\x15\x04\x0f\n\x0c\n\x05\x05\0\x02\
-    \x12\x02\x12\x03\x15\x12\x15\n\x0b\n\x04\x05\0\x02\x13\x12\x03\x16\x04\
-    \x15\n\x0c\n\x05\x05\0\x02\x13\x01\x12\x03\x16\x04\x0e\n\x0c\n\x05\x05\0\
-    \x02\x13\x02\x12\x03\x16\x11\x14\n\x0b\n\x04\x05\0\x02\x14\x12\x03\x17\
-    \x04\x14\n\x0c\n\x05\x05\0\x02\x14\x01\x12\x03\x17\x04\r\n\x0c\n\x05\x05\
-    \0\x02\x14\x02\x12\x03\x17\x10\x13\n\x0b\n\x04\x05\0\x02\x15\x12\x03\x18\
-    \x04\x19\n\x0c\n\x05\x05\0\x02\x15\x01\x12\x03\x18\x04\x11\n\x0c\n\x05\
-    \x05\0\x02\x15\x02\x12\x03\x18\x14\x18b\x06proto3\
+    \r\n\x08OpenView\x10\xcd\x01\x12\x0e\n\tCloseView\x10\xce\x01\x12\x12\n\
+    \rApplyDocDelta\x10\xcf\x01\x12\x0e\n\tReadTrash\x10\xac\x02\x12\x11\n\
+    \x0cPutbackTrash\x10\xad\x02\x12\x10\n\x0bDeleteTrash\x10\xae\x02\x12\
+    \x0f\n\nRestoreAll\x10\xaf\x02\x12\x0e\n\tDeleteAll\x10\xb0\x02\x12\x12\
+    \n\rInitWorkspace\x10\xe8\x07J\xd9\x07\n\x06\x12\x04\0\0\x1a\x01\n\x08\n\
+    \x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x1a\x01\n\n\n\x03\
+    \x05\0\x01\x12\x03\x02\x05\x13\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\
+    \x18\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x13\n\x0c\n\x05\x05\0\
+    \x02\0\x02\x12\x03\x03\x16\x17\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\
+    \x19\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\x18\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\
+    \x04\x17\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\x16\n\x0b\n\x04\x05\0\x02\x03\x12\x03\
+    \x06\x04\x18\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\x17\n\x0b\n\x04\x05\0\x02\x04\x12\
+    \x03\x07\x04\x16\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x11\n\x0c\
+    \n\x05\x05\0\x02\x04\x02\x12\x03\x07\x14\x15\n\x0b\n\x04\x05\0\x02\x05\
+    \x12\x03\x08\x04\x1a\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x15\n\
+    \x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x08\x18\x19\n\x0b\n\x04\x05\0\x02\
+    \x06\x12\x03\t\x04\x14\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\t\x04\r\n\
+    \x0c\n\x05\x05\0\x02\x06\x02\x12\x03\t\x10\x13\n\x0b\n\x04\x05\0\x02\x07\
+    \x12\x03\n\x04\x14\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\n\x04\r\n\x0c\n\
+    \x05\x05\0\x02\x07\x02\x12\x03\n\x10\x13\n\x0b\n\x04\x05\0\x02\x08\x12\
+    \x03\x0b\x04\x12\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\x0b\n\x0c\
+    \n\x05\x05\0\x02\x08\x02\x12\x03\x0b\x0e\x11\n\x0b\n\x04\x05\0\x02\t\x12\
+    \x03\x0c\x04\x14\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x0c\x04\r\n\x0c\n\
+    \x05\x05\0\x02\t\x02\x12\x03\x0c\x10\x13\n\x0b\n\x04\x05\0\x02\n\x12\x03\
+    \r\x04\x15\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\r\x04\x0e\n\x0c\n\x05\x05\
+    \0\x02\n\x02\x12\x03\r\x11\x14\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x0e\x04\
+    \x13\n\x0c\n\x05\x05\0\x02\x0b\x01\x12\x03\x0e\x04\x0c\n\x0c\n\x05\x05\0\
+    \x02\x0b\x02\x12\x03\x0e\x0f\x12\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\x0f\
+    \x04\x15\n\x0c\n\x05\x05\0\x02\x0c\x01\x12\x03\x0f\x04\x0e\n\x0c\n\x05\
+    \x05\0\x02\x0c\x02\x12\x03\x0f\x11\x14\n\x0b\n\x04\x05\0\x02\r\x12\x03\
+    \x10\x04\x15\n\x0c\n\x05\x05\0\x02\r\x01\x12\x03\x10\x04\x0e\n\x0c\n\x05\
+    \x05\0\x02\r\x02\x12\x03\x10\x11\x14\n\x0b\n\x04\x05\0\x02\x0e\x12\x03\
+    \x11\x04\x13\n\x0c\n\x05\x05\0\x02\x0e\x01\x12\x03\x11\x04\x0c\n\x0c\n\
+    \x05\x05\0\x02\x0e\x02\x12\x03\x11\x0f\x12\n\x0b\n\x04\x05\0\x02\x0f\x12\
+    \x03\x12\x04\x14\n\x0c\n\x05\x05\0\x02\x0f\x01\x12\x03\x12\x04\r\n\x0c\n\
+    \x05\x05\0\x02\x0f\x02\x12\x03\x12\x10\x13\n\x0b\n\x04\x05\0\x02\x10\x12\
+    \x03\x13\x04\x18\n\x0c\n\x05\x05\0\x02\x10\x01\x12\x03\x13\x04\x11\n\x0c\
+    \n\x05\x05\0\x02\x10\x02\x12\x03\x13\x14\x17\n\x0b\n\x04\x05\0\x02\x11\
+    \x12\x03\x14\x04\x14\n\x0c\n\x05\x05\0\x02\x11\x01\x12\x03\x14\x04\r\n\
+    \x0c\n\x05\x05\0\x02\x11\x02\x12\x03\x14\x10\x13\n\x0b\n\x04\x05\0\x02\
+    \x12\x12\x03\x15\x04\x17\n\x0c\n\x05\x05\0\x02\x12\x01\x12\x03\x15\x04\
+    \x10\n\x0c\n\x05\x05\0\x02\x12\x02\x12\x03\x15\x13\x16\n\x0b\n\x04\x05\0\
+    \x02\x13\x12\x03\x16\x04\x16\n\x0c\n\x05\x05\0\x02\x13\x01\x12\x03\x16\
+    \x04\x0f\n\x0c\n\x05\x05\0\x02\x13\x02\x12\x03\x16\x12\x15\n\x0b\n\x04\
+    \x05\0\x02\x14\x12\x03\x17\x04\x15\n\x0c\n\x05\x05\0\x02\x14\x01\x12\x03\
+    \x17\x04\x0e\n\x0c\n\x05\x05\0\x02\x14\x02\x12\x03\x17\x11\x14\n\x0b\n\
+    \x04\x05\0\x02\x15\x12\x03\x18\x04\x14\n\x0c\n\x05\x05\0\x02\x15\x01\x12\
+    \x03\x18\x04\r\n\x0c\n\x05\x05\0\x02\x15\x02\x12\x03\x18\x10\x13\n\x0b\n\
+    \x04\x05\0\x02\x16\x12\x03\x19\x04\x19\n\x0c\n\x05\x05\0\x02\x16\x01\x12\
+    \x03\x19\x04\x11\n\x0c\n\x05\x05\0\x02\x16\x02\x12\x03\x19\x14\x18b\x06p\
+    roto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 0 - 3
rust-lib/flowy-workspace/src/protobuf/model/mod.rs

@@ -3,9 +3,6 @@
 mod view_update; 
 pub use view_update::*; 
 
-mod view_delete; 
-pub use view_delete::*; 
-
 mod app_query; 
 pub use app_query::*; 
 

+ 0 - 368
rust-lib/flowy-workspace/src/protobuf/model/view_delete.rs

@@ -1,368 +0,0 @@
-// This file is generated by rust-protobuf 2.22.1. Do not edit
-// @generated
-
-// https://github.com/rust-lang/rust-clippy/issues/702
-#![allow(unknown_lints)]
-#![allow(clippy::all)]
-
-#![allow(unused_attributes)]
-#![cfg_attr(rustfmt, rustfmt::skip)]
-
-#![allow(box_pointers)]
-#![allow(dead_code)]
-#![allow(missing_docs)]
-#![allow(non_camel_case_types)]
-#![allow(non_snake_case)]
-#![allow(non_upper_case_globals)]
-#![allow(trivial_casts)]
-#![allow(unused_imports)]
-#![allow(unused_results)]
-//! Generated file from `view_delete.proto`
-
-/// Generated files are compatible only with the same version
-/// of protobuf runtime.
-// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
-
-#[derive(PartialEq,Clone,Default)]
-pub struct DeleteViewRequest {
-    // message fields
-    pub view_ids: ::protobuf::RepeatedField<::std::string::String>,
-    // special fields
-    pub unknown_fields: ::protobuf::UnknownFields,
-    pub cached_size: ::protobuf::CachedSize,
-}
-
-impl<'a> ::std::default::Default for &'a DeleteViewRequest {
-    fn default() -> &'a DeleteViewRequest {
-        <DeleteViewRequest as ::protobuf::Message>::default_instance()
-    }
-}
-
-impl DeleteViewRequest {
-    pub fn new() -> DeleteViewRequest {
-        ::std::default::Default::default()
-    }
-
-    // repeated string view_ids = 1;
-
-
-    pub fn get_view_ids(&self) -> &[::std::string::String] {
-        &self.view_ids
-    }
-    pub fn clear_view_ids(&mut self) {
-        self.view_ids.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_view_ids(&mut self, v: ::protobuf::RepeatedField<::std::string::String>) {
-        self.view_ids = v;
-    }
-
-    // Mutable pointer to the field.
-    pub fn mut_view_ids(&mut self) -> &mut ::protobuf::RepeatedField<::std::string::String> {
-        &mut self.view_ids
-    }
-
-    // Take field
-    pub fn take_view_ids(&mut self) -> ::protobuf::RepeatedField<::std::string::String> {
-        ::std::mem::replace(&mut self.view_ids, ::protobuf::RepeatedField::new())
-    }
-}
-
-impl ::protobuf::Message for DeleteViewRequest {
-    fn is_initialized(&self) -> bool {
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        while !is.eof()? {
-            let (field_number, wire_type) = is.read_tag_unpack()?;
-            match field_number {
-                1 => {
-                    ::protobuf::rt::read_repeated_string_into(wire_type, is, &mut self.view_ids)?;
-                },
-                _ => {
-                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u32 {
-        let mut my_size = 0;
-        for value in &self.view_ids {
-            my_size += ::protobuf::rt::string_size(1, &value);
-        };
-        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
-        self.cached_size.set(my_size);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        for v in &self.view_ids {
-            os.write_string(1, &v)?;
-        };
-        os.write_unknown_fields(self.get_unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn get_cached_size(&self) -> u32 {
-        self.cached_size.get()
-    }
-
-    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
-        &self.unknown_fields
-    }
-
-    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
-        &mut self.unknown_fields
-    }
-
-    fn as_any(&self) -> &dyn (::std::any::Any) {
-        self as &dyn (::std::any::Any)
-    }
-    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
-        self as &mut dyn (::std::any::Any)
-    }
-    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
-        self
-    }
-
-    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
-        Self::descriptor_static()
-    }
-
-    fn new() -> DeleteViewRequest {
-        DeleteViewRequest::new()
-    }
-
-    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
-        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
-        descriptor.get(|| {
-            let mut fields = ::std::vec::Vec::new();
-            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "view_ids",
-                |m: &DeleteViewRequest| { &m.view_ids },
-                |m: &mut DeleteViewRequest| { &mut m.view_ids },
-            ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<DeleteViewRequest>(
-                "DeleteViewRequest",
-                fields,
-                file_descriptor_proto()
-            )
-        })
-    }
-
-    fn default_instance() -> &'static DeleteViewRequest {
-        static instance: ::protobuf::rt::LazyV2<DeleteViewRequest> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(DeleteViewRequest::new)
-    }
-}
-
-impl ::protobuf::Clear for DeleteViewRequest {
-    fn clear(&mut self) {
-        self.view_ids.clear();
-        self.unknown_fields.clear();
-    }
-}
-
-impl ::std::fmt::Debug for DeleteViewRequest {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-        ::protobuf::text_format::fmt(self, f)
-    }
-}
-
-impl ::protobuf::reflect::ProtobufValue for DeleteViewRequest {
-    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
-        ::protobuf::reflect::ReflectValueRef::Message(self)
-    }
-}
-
-#[derive(PartialEq,Clone,Default)]
-pub struct DeleteViewParams {
-    // message fields
-    pub view_ids: ::protobuf::RepeatedField<::std::string::String>,
-    // special fields
-    pub unknown_fields: ::protobuf::UnknownFields,
-    pub cached_size: ::protobuf::CachedSize,
-}
-
-impl<'a> ::std::default::Default for &'a DeleteViewParams {
-    fn default() -> &'a DeleteViewParams {
-        <DeleteViewParams as ::protobuf::Message>::default_instance()
-    }
-}
-
-impl DeleteViewParams {
-    pub fn new() -> DeleteViewParams {
-        ::std::default::Default::default()
-    }
-
-    // repeated string view_ids = 1;
-
-
-    pub fn get_view_ids(&self) -> &[::std::string::String] {
-        &self.view_ids
-    }
-    pub fn clear_view_ids(&mut self) {
-        self.view_ids.clear();
-    }
-
-    // Param is passed by value, moved
-    pub fn set_view_ids(&mut self, v: ::protobuf::RepeatedField<::std::string::String>) {
-        self.view_ids = v;
-    }
-
-    // Mutable pointer to the field.
-    pub fn mut_view_ids(&mut self) -> &mut ::protobuf::RepeatedField<::std::string::String> {
-        &mut self.view_ids
-    }
-
-    // Take field
-    pub fn take_view_ids(&mut self) -> ::protobuf::RepeatedField<::std::string::String> {
-        ::std::mem::replace(&mut self.view_ids, ::protobuf::RepeatedField::new())
-    }
-}
-
-impl ::protobuf::Message for DeleteViewParams {
-    fn is_initialized(&self) -> bool {
-        true
-    }
-
-    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        while !is.eof()? {
-            let (field_number, wire_type) = is.read_tag_unpack()?;
-            match field_number {
-                1 => {
-                    ::protobuf::rt::read_repeated_string_into(wire_type, is, &mut self.view_ids)?;
-                },
-                _ => {
-                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
-                },
-            };
-        }
-        ::std::result::Result::Ok(())
-    }
-
-    // Compute sizes of nested messages
-    #[allow(unused_variables)]
-    fn compute_size(&self) -> u32 {
-        let mut my_size = 0;
-        for value in &self.view_ids {
-            my_size += ::protobuf::rt::string_size(1, &value);
-        };
-        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
-        self.cached_size.set(my_size);
-        my_size
-    }
-
-    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        for v in &self.view_ids {
-            os.write_string(1, &v)?;
-        };
-        os.write_unknown_fields(self.get_unknown_fields())?;
-        ::std::result::Result::Ok(())
-    }
-
-    fn get_cached_size(&self) -> u32 {
-        self.cached_size.get()
-    }
-
-    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
-        &self.unknown_fields
-    }
-
-    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
-        &mut self.unknown_fields
-    }
-
-    fn as_any(&self) -> &dyn (::std::any::Any) {
-        self as &dyn (::std::any::Any)
-    }
-    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
-        self as &mut dyn (::std::any::Any)
-    }
-    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
-        self
-    }
-
-    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
-        Self::descriptor_static()
-    }
-
-    fn new() -> DeleteViewParams {
-        DeleteViewParams::new()
-    }
-
-    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
-        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
-        descriptor.get(|| {
-            let mut fields = ::std::vec::Vec::new();
-            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "view_ids",
-                |m: &DeleteViewParams| { &m.view_ids },
-                |m: &mut DeleteViewParams| { &mut m.view_ids },
-            ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<DeleteViewParams>(
-                "DeleteViewParams",
-                fields,
-                file_descriptor_proto()
-            )
-        })
-    }
-
-    fn default_instance() -> &'static DeleteViewParams {
-        static instance: ::protobuf::rt::LazyV2<DeleteViewParams> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(DeleteViewParams::new)
-    }
-}
-
-impl ::protobuf::Clear for DeleteViewParams {
-    fn clear(&mut self) {
-        self.view_ids.clear();
-        self.unknown_fields.clear();
-    }
-}
-
-impl ::std::fmt::Debug for DeleteViewParams {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-        ::protobuf::text_format::fmt(self, f)
-    }
-}
-
-impl ::protobuf::reflect::ProtobufValue for DeleteViewParams {
-    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
-        ::protobuf::reflect::ReflectValueRef::Message(self)
-    }
-}
-
-static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x11view_delete.proto\".\n\x11DeleteViewRequest\x12\x19\n\x08view_ids\
-    \x18\x01\x20\x03(\tR\x07viewIds\"-\n\x10DeleteViewParams\x12\x19\n\x08vi\
-    ew_ids\x18\x01\x20\x03(\tR\x07viewIdsJ\xcc\x01\n\x06\x12\x04\0\0\x07\x01\
-    \n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x04\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!\n\x0c\n\x05\x04\0\x02\0\x04\x12\x03\x03\x04\x0c\n\x0c\n\x05\
-    \x04\0\x02\0\x05\x12\x03\x03\r\x13\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\
-    \x03\x14\x1c\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x1f\x20\n\n\n\x02\
-    \x04\x01\x12\x04\x05\0\x07\x01\n\n\n\x03\x04\x01\x01\x12\x03\x05\x08\x18\
-    \n\x0b\n\x04\x04\x01\x02\0\x12\x03\x06\x04!\n\x0c\n\x05\x04\x01\x02\0\
-    \x04\x12\x03\x06\x04\x0c\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x06\r\x13\
-    \n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x06\x14\x1c\n\x0c\n\x05\x04\x01\
-    \x02\0\x03\x12\x03\x06\x1f\x20b\x06proto3\
-";
-
-static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
-
-fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
-    ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
-}
-
-pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
-    file_descriptor_proto_lazy.get(|| {
-        parse_descriptor_proto()
-    })
-}

+ 83 - 83
rust-lib/flowy-workspace/src/protobuf/model/view_query.rs

@@ -26,7 +26,7 @@
 #[derive(PartialEq,Clone,Default)]
 pub struct QueryViewRequest {
     // message fields
-    pub view_id: ::std::string::String,
+    pub view_ids: ::protobuf::RepeatedField<::std::string::String>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -43,30 +43,29 @@ impl QueryViewRequest {
         ::std::default::Default::default()
     }
 
-    // string view_id = 1;
+    // repeated string view_ids = 1;
 
 
-    pub fn get_view_id(&self) -> &str {
-        &self.view_id
+    pub fn get_view_ids(&self) -> &[::std::string::String] {
+        &self.view_ids
     }
-    pub fn clear_view_id(&mut self) {
-        self.view_id.clear();
+    pub fn clear_view_ids(&mut self) {
+        self.view_ids.clear();
     }
 
     // Param is passed by value, moved
-    pub fn set_view_id(&mut self, v: ::std::string::String) {
-        self.view_id = v;
+    pub fn set_view_ids(&mut self, v: ::protobuf::RepeatedField<::std::string::String>) {
+        self.view_ids = v;
     }
 
     // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_view_id(&mut self) -> &mut ::std::string::String {
-        &mut self.view_id
+    pub fn mut_view_ids(&mut self) -> &mut ::protobuf::RepeatedField<::std::string::String> {
+        &mut self.view_ids
     }
 
     // Take field
-    pub fn take_view_id(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.view_id, ::std::string::String::new())
+    pub fn take_view_ids(&mut self) -> ::protobuf::RepeatedField<::std::string::String> {
+        ::std::mem::replace(&mut self.view_ids, ::protobuf::RepeatedField::new())
     }
 }
 
@@ -80,7 +79,7 @@ impl ::protobuf::Message for QueryViewRequest {
             let (field_number, wire_type) = is.read_tag_unpack()?;
             match field_number {
                 1 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.view_id)?;
+                    ::protobuf::rt::read_repeated_string_into(wire_type, is, &mut self.view_ids)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -94,18 +93,18 @@ impl ::protobuf::Message for QueryViewRequest {
     #[allow(unused_variables)]
     fn compute_size(&self) -> u32 {
         let mut my_size = 0;
-        if !self.view_id.is_empty() {
-            my_size += ::protobuf::rt::string_size(1, &self.view_id);
-        }
+        for value in &self.view_ids {
+            my_size += ::protobuf::rt::string_size(1, &value);
+        };
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
         my_size
     }
 
     fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        if !self.view_id.is_empty() {
-            os.write_string(1, &self.view_id)?;
-        }
+        for v in &self.view_ids {
+            os.write_string(1, &v)?;
+        };
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
     }
@@ -144,10 +143,10 @@ impl ::protobuf::Message for QueryViewRequest {
         static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
         descriptor.get(|| {
             let mut fields = ::std::vec::Vec::new();
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "view_id",
-                |m: &QueryViewRequest| { &m.view_id },
-                |m: &mut QueryViewRequest| { &mut m.view_id },
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "view_ids",
+                |m: &QueryViewRequest| { &m.view_ids },
+                |m: &mut QueryViewRequest| { &mut m.view_ids },
             ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<QueryViewRequest>(
                 "QueryViewRequest",
@@ -165,7 +164,7 @@ impl ::protobuf::Message for QueryViewRequest {
 
 impl ::protobuf::Clear for QueryViewRequest {
     fn clear(&mut self) {
-        self.view_id.clear();
+        self.view_ids.clear();
         self.unknown_fields.clear();
     }
 }
@@ -342,53 +341,52 @@ impl ::protobuf::reflect::ProtobufValue for ViewIdentifier {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct OpenViewRequest {
+pub struct ViewIdentifiers {
     // message fields
-    pub view_id: ::std::string::String,
+    pub view_ids: ::protobuf::RepeatedField<::std::string::String>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a OpenViewRequest {
-    fn default() -> &'a OpenViewRequest {
-        <OpenViewRequest as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a ViewIdentifiers {
+    fn default() -> &'a ViewIdentifiers {
+        <ViewIdentifiers as ::protobuf::Message>::default_instance()
     }
 }
 
-impl OpenViewRequest {
-    pub fn new() -> OpenViewRequest {
+impl ViewIdentifiers {
+    pub fn new() -> ViewIdentifiers {
         ::std::default::Default::default()
     }
 
-    // string view_id = 1;
+    // repeated string view_ids = 1;
 
 
-    pub fn get_view_id(&self) -> &str {
-        &self.view_id
+    pub fn get_view_ids(&self) -> &[::std::string::String] {
+        &self.view_ids
     }
-    pub fn clear_view_id(&mut self) {
-        self.view_id.clear();
+    pub fn clear_view_ids(&mut self) {
+        self.view_ids.clear();
     }
 
     // Param is passed by value, moved
-    pub fn set_view_id(&mut self, v: ::std::string::String) {
-        self.view_id = v;
+    pub fn set_view_ids(&mut self, v: ::protobuf::RepeatedField<::std::string::String>) {
+        self.view_ids = v;
     }
 
     // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
-    pub fn mut_view_id(&mut self) -> &mut ::std::string::String {
-        &mut self.view_id
+    pub fn mut_view_ids(&mut self) -> &mut ::protobuf::RepeatedField<::std::string::String> {
+        &mut self.view_ids
     }
 
     // Take field
-    pub fn take_view_id(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.view_id, ::std::string::String::new())
+    pub fn take_view_ids(&mut self) -> ::protobuf::RepeatedField<::std::string::String> {
+        ::std::mem::replace(&mut self.view_ids, ::protobuf::RepeatedField::new())
     }
 }
 
-impl ::protobuf::Message for OpenViewRequest {
+impl ::protobuf::Message for ViewIdentifiers {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -398,7 +396,7 @@ impl ::protobuf::Message for OpenViewRequest {
             let (field_number, wire_type) = is.read_tag_unpack()?;
             match field_number {
                 1 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.view_id)?;
+                    ::protobuf::rt::read_repeated_string_into(wire_type, is, &mut self.view_ids)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -412,18 +410,18 @@ impl ::protobuf::Message for OpenViewRequest {
     #[allow(unused_variables)]
     fn compute_size(&self) -> u32 {
         let mut my_size = 0;
-        if !self.view_id.is_empty() {
-            my_size += ::protobuf::rt::string_size(1, &self.view_id);
-        }
+        for value in &self.view_ids {
+            my_size += ::protobuf::rt::string_size(1, &value);
+        };
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
         my_size
     }
 
     fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
-        if !self.view_id.is_empty() {
-            os.write_string(1, &self.view_id)?;
-        }
+        for v in &self.view_ids {
+            os.write_string(1, &v)?;
+        };
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
     }
@@ -454,69 +452,71 @@ impl ::protobuf::Message for OpenViewRequest {
         Self::descriptor_static()
     }
 
-    fn new() -> OpenViewRequest {
-        OpenViewRequest::new()
+    fn new() -> ViewIdentifiers {
+        ViewIdentifiers::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
         static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
         descriptor.get(|| {
             let mut fields = ::std::vec::Vec::new();
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "view_id",
-                |m: &OpenViewRequest| { &m.view_id },
-                |m: &mut OpenViewRequest| { &mut m.view_id },
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "view_ids",
+                |m: &ViewIdentifiers| { &m.view_ids },
+                |m: &mut ViewIdentifiers| { &mut m.view_ids },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<OpenViewRequest>(
-                "OpenViewRequest",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<ViewIdentifiers>(
+                "ViewIdentifiers",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static OpenViewRequest {
-        static instance: ::protobuf::rt::LazyV2<OpenViewRequest> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(OpenViewRequest::new)
+    fn default_instance() -> &'static ViewIdentifiers {
+        static instance: ::protobuf::rt::LazyV2<ViewIdentifiers> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(ViewIdentifiers::new)
     }
 }
 
-impl ::protobuf::Clear for OpenViewRequest {
+impl ::protobuf::Clear for ViewIdentifiers {
     fn clear(&mut self) {
-        self.view_id.clear();
+        self.view_ids.clear();
         self.unknown_fields.clear();
     }
 }
 
-impl ::std::fmt::Debug for OpenViewRequest {
+impl ::std::fmt::Debug for ViewIdentifiers {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for OpenViewRequest {
+impl ::protobuf::reflect::ProtobufValue for ViewIdentifiers {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x10view_query.proto\"+\n\x10QueryViewRequest\x12\x17\n\x07view_id\x18\
-    \x01\x20\x01(\tR\x06viewId\")\n\x0eViewIdentifier\x12\x17\n\x07view_id\
-    \x18\x01\x20\x01(\tR\x06viewId\"*\n\x0fOpenViewRequest\x12\x17\n\x07view\
-    _id\x18\x01\x20\x01(\tR\x06viewIdJ\xff\x01\n\x06\x12\x04\0\0\n\x01\n\x08\
-    \n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x04\x01\n\n\n\
-    \x03\x04\0\x01\x12\x03\x02\x08\x18\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\
-    \x04\x17\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\x12\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\
-    \x15\x16\n\n\n\x02\x04\x01\x12\x04\x05\0\x07\x01\n\n\n\x03\x04\x01\x01\
-    \x12\x03\x05\x08\x16\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x06\x04\x17\n\x0c\
-    \n\x05\x04\x01\x02\0\x05\x12\x03\x06\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\
-    \x12\x03\x06\x0b\x12\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\x06\x15\x16\n\
-    \n\n\x02\x04\x02\x12\x04\x08\0\n\x01\n\n\n\x03\x04\x02\x01\x12\x03\x08\
-    \x08\x17\n\x0b\n\x04\x04\x02\x02\0\x12\x03\t\x04\x17\n\x0c\n\x05\x04\x02\
-    \x02\0\x05\x12\x03\t\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\t\x0b\
-    \x12\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\t\x15\x16b\x06proto3\
+    \n\x10view_query.proto\"-\n\x10QueryViewRequest\x12\x19\n\x08view_ids\
+    \x18\x01\x20\x03(\tR\x07viewIds\")\n\x0eViewIdentifier\x12\x17\n\x07view\
+    _id\x18\x01\x20\x01(\tR\x06viewId\",\n\x0fViewIdentifiers\x12\x19\n\x08v\
+    iew_ids\x18\x01\x20\x03(\tR\x07viewIdsJ\x9b\x02\n\x06\x12\x04\0\0\n\x01\
+    \n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x04\x01\n\
+    \n\n\x03\x04\0\x01\x12\x03\x02\x08\x18\n\x0b\n\x04\x04\0\x02\0\x12\x03\
+    \x03\x04!\n\x0c\n\x05\x04\0\x02\0\x04\x12\x03\x03\x04\x0c\n\x0c\n\x05\
+    \x04\0\x02\0\x05\x12\x03\x03\r\x13\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\
+    \x03\x14\x1c\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x1f\x20\n\n\n\x02\
+    \x04\x01\x12\x04\x05\0\x07\x01\n\n\n\x03\x04\x01\x01\x12\x03\x05\x08\x16\
+    \n\x0b\n\x04\x04\x01\x02\0\x12\x03\x06\x04\x17\n\x0c\n\x05\x04\x01\x02\0\
+    \x05\x12\x03\x06\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x06\x0b\x12\
+    \n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\x06\x15\x16\n\n\n\x02\x04\x02\x12\
+    \x04\x08\0\n\x01\n\n\n\x03\x04\x02\x01\x12\x03\x08\x08\x17\n\x0b\n\x04\
+    \x04\x02\x02\0\x12\x03\t\x04!\n\x0c\n\x05\x04\x02\x02\0\x04\x12\x03\t\
+    \x04\x0c\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\t\r\x13\n\x0c\n\x05\x04\
+    \x02\x02\0\x01\x12\x03\t\x14\x1c\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\t\
+    \x1f\x20b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 2 - 1
rust-lib/flowy-workspace/src/protobuf/proto/event.proto

@@ -16,7 +16,8 @@ enum WorkspaceEvent {
     UpdateView = 203;
     DeleteView = 204;
     OpenView = 205;
-    ApplyDocDelta = 206;
+    CloseView = 206;
+    ApplyDocDelta = 207;
     ReadTrash = 300;
     PutbackTrash = 301;
     DeleteTrash = 302;

+ 0 - 8
rust-lib/flowy-workspace/src/protobuf/proto/view_delete.proto

@@ -1,8 +0,0 @@
-syntax = "proto3";
-
-message DeleteViewRequest {
-    repeated string view_ids = 1;
-}
-message DeleteViewParams {
-    repeated string view_ids = 1;
-}

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

@@ -1,11 +1,11 @@
 syntax = "proto3";
 
 message QueryViewRequest {
-    string view_id = 1;
+    repeated string view_ids = 1;
 }
 message ViewIdentifier {
     string view_id = 1;
 }
-message OpenViewRequest {
-    string view_id = 1;
+message ViewIdentifiers {
+    repeated string view_ids = 1;
 }

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

@@ -10,7 +10,7 @@ use crate::{
     entities::{
         app::{App, AppIdentifier, CreateAppParams, DeleteAppParams, UpdateAppParams},
         trash::{RepeatedTrash, TrashIdentifiers},
-        view::{CreateViewParams, DeleteViewParams, UpdateViewParams, View, ViewIdentifier},
+        view::{CreateViewParams, UpdateViewParams, View, ViewIdentifier, ViewIdentifiers},
         workspace::{
             CreateWorkspaceParams,
             DeleteWorkspaceParams,
@@ -47,7 +47,7 @@ pub trait WorkspaceServerAPI {
 
     fn read_view(&self, token: &str, params: ViewIdentifier) -> ResultFuture<Option<View>, WorkspaceError>;
 
-    fn delete_view(&self, token: &str, params: DeleteViewParams) -> ResultFuture<(), WorkspaceError>;
+    fn delete_view(&self, token: &str, params: ViewIdentifiers) -> ResultFuture<(), WorkspaceError>;
 
     fn update_view(&self, token: &str, params: UpdateViewParams) -> ResultFuture<(), WorkspaceError>;
 

+ 3 - 3
rust-lib/flowy-workspace/src/services/server/server_api.rs

@@ -2,7 +2,7 @@ use crate::{
     entities::{
         app::{App, AppIdentifier, CreateAppParams, DeleteAppParams, UpdateAppParams},
         trash::{RepeatedTrash, TrashIdentifiers},
-        view::{CreateViewParams, DeleteViewParams, UpdateViewParams, View, ViewIdentifier},
+        view::{CreateViewParams, UpdateViewParams, View, ViewIdentifier, ViewIdentifiers},
         workspace::{
             CreateWorkspaceParams,
             DeleteWorkspaceParams,
@@ -67,7 +67,7 @@ impl WorkspaceServerAPI for WorkspaceServer {
         ResultFuture::new(async move { read_view_request(&token, params, &url).await })
     }
 
-    fn delete_view(&self, token: &str, params: DeleteViewParams) -> ResultFuture<(), WorkspaceError> {
+    fn delete_view(&self, token: &str, params: ViewIdentifiers) -> ResultFuture<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.view_url();
         ResultFuture::new(async move { delete_view_request(&token, params, &url).await })
@@ -256,7 +256,7 @@ pub async fn update_view_request(token: &str, params: UpdateViewParams, url: &st
     Ok(())
 }
 
-pub async fn delete_view_request(token: &str, params: DeleteViewParams, url: &str) -> Result<(), WorkspaceError> {
+pub async fn delete_view_request(token: &str, params: ViewIdentifiers, url: &str) -> Result<(), WorkspaceError> {
     let _ = request_builder()
         .delete(&url.to_owned())
         .header(HEADER_TOKEN, token)

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

@@ -2,7 +2,7 @@ use crate::{
     entities::{
         app::{App, AppIdentifier, CreateAppParams, DeleteAppParams, RepeatedApp, UpdateAppParams},
         trash::{RepeatedTrash, TrashIdentifiers},
-        view::{CreateViewParams, DeleteViewParams, RepeatedView, UpdateViewParams, View, ViewIdentifier},
+        view::{CreateViewParams, RepeatedView, UpdateViewParams, View, ViewIdentifier, ViewIdentifiers},
         workspace::{
             CreateWorkspaceParams,
             DeleteWorkspaceParams,
@@ -73,7 +73,7 @@ impl WorkspaceServerAPI for WorkspaceServerMock {
         ResultFuture::new(async { Ok(None) })
     }
 
-    fn delete_view(&self, _token: &str, _params: DeleteViewParams) -> ResultFuture<(), WorkspaceError> {
+    fn delete_view(&self, _token: &str, _params: ViewIdentifiers) -> ResultFuture<(), WorkspaceError> {
         ResultFuture::new(async { Ok(()) })
     }
 

+ 17 - 16
rust-lib/flowy-workspace/src/services/trash_can.rs

@@ -33,22 +33,6 @@ impl TrashCan {
 
     pub(crate) fn init(&self) -> Result<(), WorkspaceError> { Ok(()) }
 
-    pub fn read_trash(&self, conn: &SqliteConnection) -> Result<RepeatedTrash, WorkspaceError> {
-        let repeated_trash = TrashTableSql::read_all(&*conn)?;
-
-        let _ = self.read_trash_on_server()?;
-        Ok(repeated_trash)
-    }
-
-    pub fn trash_ids(&self, conn: &SqliteConnection) -> Result<Vec<String>, WorkspaceError> {
-        let ids = TrashTableSql::read_all(&*conn)?
-            .into_inner()
-            .into_iter()
-            .map(|item| item.id)
-            .collect::<Vec<String>>();
-        Ok(ids)
-    }
-
     #[tracing::instrument(level = "debug", skip(self), fields(putback)  err)]
     pub async fn putback(&self, trash_id: &str) -> WorkspaceResult<()> {
         let (tx, mut rx) = mpsc::channel::<WorkspaceResult<()>>(1);
@@ -98,6 +82,7 @@ impl TrashCan {
     pub async fn delete_all(&self) -> WorkspaceResult<()> {
         let repeated_trash = self.delete_all_trash_on_local()?;
         let identifiers: TrashIdentifiers = repeated_trash.items.clone().into();
+
         let (tx, mut rx) = mpsc::channel::<WorkspaceResult<()>>(1);
         let _ = self.notify.send(TrashEvent::Delete(identifiers, tx));
         let _ = rx.recv().await;
@@ -170,6 +155,22 @@ impl TrashCan {
     }
 
     pub fn subscribe(&self) -> broadcast::Receiver<TrashEvent> { self.notify.subscribe() }
+
+    pub fn read_trash(&self, conn: &SqliteConnection) -> Result<RepeatedTrash, WorkspaceError> {
+        let repeated_trash = TrashTableSql::read_all(&*conn)?;
+
+        let _ = self.read_trash_on_server()?;
+        Ok(repeated_trash)
+    }
+
+    pub fn trash_ids(&self, conn: &SqliteConnection) -> Result<Vec<String>, WorkspaceError> {
+        let ids = TrashTableSql::read_all(&*conn)?
+            .into_inner()
+            .into_iter()
+            .map(|item| item.id)
+            .collect::<Vec<String>>();
+        Ok(ids)
+    }
 }
 
 impl TrashCan {

+ 9 - 4
rust-lib/flowy-workspace/src/services/view_controller.rs

@@ -16,7 +16,7 @@ use crate::{
 };
 use flowy_database::SqliteConnection;
 use flowy_document::{
-    entities::doc::{CreateDocParams, DocDelta, DocIdentifier},
+    entities::doc::{DocDelta, DocIdentifier},
     module::FlowyDocument,
 };
 
@@ -113,6 +113,12 @@ impl ViewController {
         Ok(edit_context.delta().await.map_err(internal_error)?)
     }
 
+    #[tracing::instrument(level = "debug", skip(self), err)]
+    pub(crate) async fn close_view(&self, params: DocIdentifier) -> Result<(), WorkspaceError> {
+        let _ = self.document.close(params).await?;
+        Ok(())
+    }
+
     // belong_to_id will be the app_id or view_id.
     #[tracing::instrument(level = "debug", skip(self), err)]
     pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, WorkspaceError> {
@@ -211,11 +217,10 @@ impl ViewController {
                 let mut stream = Box::pin(rx.recv().into_stream().filter_map(|result| async move {
                     match result {
                         Ok(event) => event.select(TrashType::View),
-                        Err(_) => None,
+                        Err(_e) => None,
                     }
                 }));
-                let event: Option<TrashEvent> = stream.next().await;
-                match event {
+                match stream.next().await {
                     Some(event) => {
                         handle_trash_event(database.clone(), document.clone(), trash_can.clone(), event).await
                     },

+ 35 - 52
rust-lib/flowy-workspace/tests/workspace/view_test.rs

@@ -4,7 +4,6 @@ use flowy_workspace::entities::{
     trash::{TrashIdentifier, TrashType},
     view::*,
 };
-use tokio::time::{sleep, Duration};
 
 #[tokio::test]
 #[should_panic]
@@ -13,8 +12,10 @@ async fn view_delete() {
     let _ = test.init_user();
 
     let test = ViewTest::new(&test).await;
-    test.delete_view(vec![test.view.id.clone()]).await;
-    let query = QueryViewRequest::new(&test.view.id);
+    test.delete_views(vec![test.view.id.clone()]).await;
+    let query = QueryViewRequest {
+        view_ids: vec![test.view.id.clone()],
+    };
     let _ = read_view(&test.sdk, query).await;
 }
 
@@ -24,7 +25,7 @@ async fn view_delete_then_putback() {
     let _ = test.init_user();
 
     let test = ViewTest::new(&test).await;
-    test.delete_view(vec![test.view.id.clone()]).await;
+    test.delete_views(vec![test.view.id.clone()]).await;
     putback_trash(
         &test.sdk,
         TrashIdentifier {
@@ -34,7 +35,9 @@ async fn view_delete_then_putback() {
     )
     .await;
 
-    let query = QueryViewRequest::new(&test.view.id);
+    let query = QueryViewRequest {
+        view_ids: vec![test.view.id.clone()],
+    };
     let view = read_view(&test.sdk, query).await;
     assert_eq!(&view, &test.view);
 }
@@ -45,15 +48,35 @@ async fn view_delete_all() {
     let _ = test.init_user();
 
     let test = ViewTest::new(&test).await;
-    let _ = create_view(&test.sdk, &test.app.id).await;
-    let _ = create_view(&test.sdk, &test.app.id).await;
+    let view1 = test.view.clone();
+    let view2 = create_view(&test.sdk, &test.app.id).await;
+    let view3 = create_view(&test.sdk, &test.app.id).await;
+    let view_ids = vec![view1.id.clone(), view2.id.clone(), view3.id.clone()];
+
     let query = QueryAppRequest::new(&test.app.id);
     let app = read_app(&test.sdk, query.clone()).await;
-    assert_eq!(app.belongings.len(), 3);
-    test.delete_all_view().await;
+    assert_eq!(app.belongings.len(), view_ids.len());
+    test.delete_views(view_ids.clone()).await;
+
+    assert_eq!(read_app(&test.sdk, query).await.belongings.len(), 0);
+    assert_eq!(read_trash(&test.sdk).await.len(), view_ids.len());
+}
+
+#[tokio::test]
+async fn view_delete_all_permanent() {
+    let test = FlowyTest::setup();
+    let _ = test.init_user();
+
+    let test = ViewTest::new(&test).await;
+    let view1 = test.view.clone();
+    let view2 = create_view(&test.sdk, &test.app.id).await;
 
-    sleep(Duration::from_secs(1)).await;
+    let view_ids = vec![view1.id.clone(), view2.id.clone()];
+    test.delete_views_permanent(view_ids).await;
+
+    let query = QueryAppRequest::new(&test.app.id);
     assert_eq!(read_app(&test.sdk, query).await.belongings.len(), 0);
+    assert_eq!(read_trash(&test.sdk).await.len(), 0);
 }
 
 #[tokio::test]
@@ -62,48 +85,8 @@ async fn view_open_doc() {
     let _ = test.init_user().await;
 
     let test = ViewTest::new(&test).await;
-    let request = OpenViewRequest {
-        view_id: test.view.id.clone(),
+    let request = QueryViewRequest {
+        view_ids: vec![test.view.id.clone()],
     };
     let _ = open_view(&test.sdk, request).await;
 }
-
-#[test]
-fn view_update_doc() {
-    // let test = ViewTest::new();
-    // let new_data = DeltaBuilder::new().insert("flutter ❤️
-    // rust").build().into_bytes(); let request = SaveViewDataRequest {
-    //     view_id: test.view.id.clone(),
-    //     data: new_data.clone(),
-    // };
-    //
-    // update_view_data(&test.sdk, request);
-    //
-    // let request = OpenViewRequest {
-    //     view_id: test.view.id.clone(),
-    // };
-    // let doc = open_view(&test.sdk, request);
-    // assert_eq!(doc.data, new_data);
-}
-
-#[test]
-fn view_update_big_doc() {
-    // let test = ViewTest::new();
-    // let new_data = DeltaBuilder::new().insert(&"flutter ❤️
-    // rust".repeat(1000000)).build().into_bytes();
-    //
-    // let request = SaveViewDataRequest {
-    //     view_id: test.view.id.clone(),
-    //     data: new_data.clone(),
-    // };
-    //
-    // update_view_data(&test.sdk, request);
-    //
-    // let doc = open_view(
-    //     &test.sdk,
-    //     OpenViewRequest {
-    //         view_id: test.view.id.clone(),
-    //     },
-    // );
-    // assert_eq!(doc.data, new_data);
-}