Przeglądaj źródła

[client]: adding trash record as delete view

appflowy 3 lat temu
rodzic
commit
495dc24ec4
32 zmienionych plików z 343 dodań i 499 usunięć
  1. 13 13
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pb.dart
  2. 5 5
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pbjson.dart
  3. 0 54
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pb.dart
  4. 2 6
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pbjson.dart
  5. 21 6
      backend/src/service/trash/router.rs
  6. 15 11
      backend/src/service/trash/trash.rs
  7. 2 2
      backend/src/service/view/router.rs
  8. 6 0
      backend/src/service/view/view.rs
  9. 37 33
      backend/tests/api/workspace.rs
  10. 1 1
      backend/tests/helper.rs
  11. 1 1
      rust-lib/flowy-derive/src/derive_cache/derive_cache.rs
  12. 1 0
      rust-lib/flowy-net/src/errors.rs
  13. 12 8
      rust-lib/flowy-test/src/workspace.rs
  14. 2 0
      rust-lib/flowy-workspace/Cargo.toml
  15. 1 1
      rust-lib/flowy-workspace/src/entities/view/view_delete.rs
  16. 10 6
      rust-lib/flowy-workspace/src/entities/view/view_query.rs
  17. 0 12
      rust-lib/flowy-workspace/src/entities/view/view_update.rs
  18. 25 15
      rust-lib/flowy-workspace/src/handlers/view_handler.rs
  19. 21 21
      rust-lib/flowy-workspace/src/protobuf/model/view_query.rs
  20. 39 164
      rust-lib/flowy-workspace/src/protobuf/model/view_update.rs
  21. 1 1
      rust-lib/flowy-workspace/src/protobuf/proto/view_query.proto
  22. 0 2
      rust-lib/flowy-workspace/src/protobuf/proto/view_update.proto
  23. 2 2
      rust-lib/flowy-workspace/src/services/server/mod.rs
  24. 3 7
      rust-lib/flowy-workspace/src/services/server/server_api.rs
  25. 2 2
      rust-lib/flowy-workspace/src/services/server/server_api_mock.rs
  26. 39 20
      rust-lib/flowy-workspace/src/services/trash_can.rs
  27. 37 62
      rust-lib/flowy-workspace/src/services/view_controller.rs
  28. 13 4
      rust-lib/flowy-workspace/src/sql_tables/trash/trash_table.rs
  29. 4 11
      rust-lib/flowy-workspace/src/sql_tables/view/view_sql.rs
  30. 16 4
      rust-lib/flowy-workspace/src/sql_tables/view/view_table.rs
  31. 10 10
      rust-lib/flowy-workspace/tests/workspace/app_test.rs
  32. 2 15
      rust-lib/flowy-workspace/tests/workspace/view_test.rs

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

@@ -56,14 +56,14 @@ class QueryViewRequest extends $pb.GeneratedMessage {
   void clearViewId() => clearField(1);
 }
 
-class QueryViewParams extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'QueryViewParams', createEmptyInstance: create)
+class ViewIdentifier extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ViewIdentifier', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
     ..hasRequiredFields = false
   ;
 
-  QueryViewParams._() : super();
-  factory QueryViewParams({
+  ViewIdentifier._() : super();
+  factory ViewIdentifier({
     $core.String? viewId,
   }) {
     final _result = create();
@@ -72,26 +72,26 @@ class QueryViewParams extends $pb.GeneratedMessage {
     }
     return _result;
   }
-  factory QueryViewParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory QueryViewParams.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory ViewIdentifier.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory ViewIdentifier.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')
-  QueryViewParams clone() => QueryViewParams()..mergeFromMessage(this);
+  ViewIdentifier clone() => ViewIdentifier()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  QueryViewParams copyWith(void Function(QueryViewParams) updates) => super.copyWith((message) => updates(message as QueryViewParams)) as QueryViewParams; // ignore: deprecated_member_use
+  ViewIdentifier copyWith(void Function(ViewIdentifier) updates) => super.copyWith((message) => updates(message as ViewIdentifier)) as ViewIdentifier; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static QueryViewParams create() => QueryViewParams._();
-  QueryViewParams createEmptyInstance() => create();
-  static $pb.PbList<QueryViewParams> createRepeated() => $pb.PbList<QueryViewParams>();
+  static ViewIdentifier create() => ViewIdentifier._();
+  ViewIdentifier createEmptyInstance() => create();
+  static $pb.PbList<ViewIdentifier> createRepeated() => $pb.PbList<ViewIdentifier>();
   @$core.pragma('dart2js:noInline')
-  static QueryViewParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<QueryViewParams>(create);
-  static QueryViewParams? _defaultInstance;
+  static ViewIdentifier getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ViewIdentifier>(create);
+  static ViewIdentifier? _defaultInstance;
 
   @$pb.TagNumber(1)
   $core.String get viewId => $_getSZ(0);

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

@@ -18,16 +18,16 @@ const QueryViewRequest$json = const {
 
 /// Descriptor for `QueryViewRequest`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List queryViewRequestDescriptor = $convert.base64Decode('ChBRdWVyeVZpZXdSZXF1ZXN0EhcKB3ZpZXdfaWQYASABKAlSBnZpZXdJZA==');
-@$core.Deprecated('Use queryViewParamsDescriptor instead')
-const QueryViewParams$json = const {
-  '1': 'QueryViewParams',
+@$core.Deprecated('Use viewIdentifierDescriptor instead')
+const ViewIdentifier$json = const {
+  '1': 'ViewIdentifier',
   '2': const [
     const {'1': 'view_id', '3': 1, '4': 1, '5': 9, '10': 'viewId'},
   ],
 };
 
-/// Descriptor for `QueryViewParams`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List queryViewParamsDescriptor = $convert.base64Decode('Cg9RdWVyeVZpZXdQYXJhbXMSFwoHdmlld19pZBgBIAEoCVIGdmlld0lk');
+/// 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',

+ 0 - 54
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pb.dart

@@ -24,11 +24,6 @@ enum UpdateViewRequest_OneOfThumbnail {
   notSet
 }
 
-enum UpdateViewRequest_OneOfIsTrash {
-  isTrash, 
-  notSet
-}
-
 class UpdateViewRequest extends $pb.GeneratedMessage {
   static const $core.Map<$core.int, UpdateViewRequest_OneOfName> _UpdateViewRequest_OneOfNameByTag = {
     2 : UpdateViewRequest_OneOfName.name,
@@ -42,20 +37,14 @@ class UpdateViewRequest extends $pb.GeneratedMessage {
     4 : UpdateViewRequest_OneOfThumbnail.thumbnail,
     0 : UpdateViewRequest_OneOfThumbnail.notSet
   };
-  static const $core.Map<$core.int, UpdateViewRequest_OneOfIsTrash> _UpdateViewRequest_OneOfIsTrashByTag = {
-    5 : UpdateViewRequest_OneOfIsTrash.isTrash,
-    0 : UpdateViewRequest_OneOfIsTrash.notSet
-  };
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UpdateViewRequest', createEmptyInstance: create)
     ..oo(0, [2])
     ..oo(1, [3])
     ..oo(2, [4])
-    ..oo(3, [5])
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
     ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
     ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
-    ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isTrash')
     ..hasRequiredFields = false
   ;
 
@@ -65,7 +54,6 @@ class UpdateViewRequest extends $pb.GeneratedMessage {
     $core.String? name,
     $core.String? desc,
     $core.String? thumbnail,
-    $core.bool? isTrash,
   }) {
     final _result = create();
     if (viewId != null) {
@@ -80,9 +68,6 @@ class UpdateViewRequest extends $pb.GeneratedMessage {
     if (thumbnail != null) {
       _result.thumbnail = thumbnail;
     }
-    if (isTrash != null) {
-      _result.isTrash = isTrash;
-    }
     return _result;
   }
   factory UpdateViewRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
@@ -115,9 +100,6 @@ class UpdateViewRequest extends $pb.GeneratedMessage {
   UpdateViewRequest_OneOfThumbnail whichOneOfThumbnail() => _UpdateViewRequest_OneOfThumbnailByTag[$_whichOneof(2)]!;
   void clearOneOfThumbnail() => clearField($_whichOneof(2));
 
-  UpdateViewRequest_OneOfIsTrash whichOneOfIsTrash() => _UpdateViewRequest_OneOfIsTrashByTag[$_whichOneof(3)]!;
-  void clearOneOfIsTrash() => clearField($_whichOneof(3));
-
   @$pb.TagNumber(1)
   $core.String get viewId => $_getSZ(0);
   @$pb.TagNumber(1)
@@ -153,15 +135,6 @@ class UpdateViewRequest extends $pb.GeneratedMessage {
   $core.bool hasThumbnail() => $_has(3);
   @$pb.TagNumber(4)
   void clearThumbnail() => clearField(4);
-
-  @$pb.TagNumber(5)
-  $core.bool get isTrash => $_getBF(4);
-  @$pb.TagNumber(5)
-  set isTrash($core.bool v) { $_setBool(4, v); }
-  @$pb.TagNumber(5)
-  $core.bool hasIsTrash() => $_has(4);
-  @$pb.TagNumber(5)
-  void clearIsTrash() => clearField(5);
 }
 
 enum UpdateViewParams_OneOfName {
@@ -179,11 +152,6 @@ enum UpdateViewParams_OneOfThumbnail {
   notSet
 }
 
-enum UpdateViewParams_OneOfIsTrash {
-  isTrash, 
-  notSet
-}
-
 class UpdateViewParams extends $pb.GeneratedMessage {
   static const $core.Map<$core.int, UpdateViewParams_OneOfName> _UpdateViewParams_OneOfNameByTag = {
     2 : UpdateViewParams_OneOfName.name,
@@ -197,20 +165,14 @@ class UpdateViewParams extends $pb.GeneratedMessage {
     4 : UpdateViewParams_OneOfThumbnail.thumbnail,
     0 : UpdateViewParams_OneOfThumbnail.notSet
   };
-  static const $core.Map<$core.int, UpdateViewParams_OneOfIsTrash> _UpdateViewParams_OneOfIsTrashByTag = {
-    5 : UpdateViewParams_OneOfIsTrash.isTrash,
-    0 : UpdateViewParams_OneOfIsTrash.notSet
-  };
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UpdateViewParams', createEmptyInstance: create)
     ..oo(0, [2])
     ..oo(1, [3])
     ..oo(2, [4])
-    ..oo(3, [5])
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
     ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
     ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
-    ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isTrash')
     ..hasRequiredFields = false
   ;
 
@@ -220,7 +182,6 @@ class UpdateViewParams extends $pb.GeneratedMessage {
     $core.String? name,
     $core.String? desc,
     $core.String? thumbnail,
-    $core.bool? isTrash,
   }) {
     final _result = create();
     if (viewId != null) {
@@ -235,9 +196,6 @@ class UpdateViewParams extends $pb.GeneratedMessage {
     if (thumbnail != null) {
       _result.thumbnail = thumbnail;
     }
-    if (isTrash != null) {
-      _result.isTrash = isTrash;
-    }
     return _result;
   }
   factory UpdateViewParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
@@ -270,9 +228,6 @@ class UpdateViewParams extends $pb.GeneratedMessage {
   UpdateViewParams_OneOfThumbnail whichOneOfThumbnail() => _UpdateViewParams_OneOfThumbnailByTag[$_whichOneof(2)]!;
   void clearOneOfThumbnail() => clearField($_whichOneof(2));
 
-  UpdateViewParams_OneOfIsTrash whichOneOfIsTrash() => _UpdateViewParams_OneOfIsTrashByTag[$_whichOneof(3)]!;
-  void clearOneOfIsTrash() => clearField($_whichOneof(3));
-
   @$pb.TagNumber(1)
   $core.String get viewId => $_getSZ(0);
   @$pb.TagNumber(1)
@@ -308,14 +263,5 @@ class UpdateViewParams extends $pb.GeneratedMessage {
   $core.bool hasThumbnail() => $_has(3);
   @$pb.TagNumber(4)
   void clearThumbnail() => clearField(4);
-
-  @$pb.TagNumber(5)
-  $core.bool get isTrash => $_getBF(4);
-  @$pb.TagNumber(5)
-  set isTrash($core.bool v) { $_setBool(4, v); }
-  @$pb.TagNumber(5)
-  $core.bool hasIsTrash() => $_has(4);
-  @$pb.TagNumber(5)
-  void clearIsTrash() => clearField(5);
 }
 

+ 2 - 6
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pbjson.dart

@@ -16,18 +16,16 @@ const UpdateViewRequest$json = const {
     const {'1': 'name', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'name'},
     const {'1': 'desc', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'desc'},
     const {'1': 'thumbnail', '3': 4, '4': 1, '5': 9, '9': 2, '10': 'thumbnail'},
-    const {'1': 'is_trash', '3': 5, '4': 1, '5': 8, '9': 3, '10': 'isTrash'},
   ],
   '8': const [
     const {'1': 'one_of_name'},
     const {'1': 'one_of_desc'},
     const {'1': 'one_of_thumbnail'},
-    const {'1': 'one_of_is_trash'},
   ],
 };
 
 /// Descriptor for `UpdateViewRequest`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List updateViewRequestDescriptor = $convert.base64Decode('ChFVcGRhdGVWaWV3UmVxdWVzdBIXCgd2aWV3X2lkGAEgASgJUgZ2aWV3SWQSFAoEbmFtZRgCIAEoCUgAUgRuYW1lEhQKBGRlc2MYAyABKAlIAVIEZGVzYxIeCgl0aHVtYm5haWwYBCABKAlIAlIJdGh1bWJuYWlsEhsKCGlzX3RyYXNoGAUgASgISANSB2lzVHJhc2hCDQoLb25lX29mX25hbWVCDQoLb25lX29mX2Rlc2NCEgoQb25lX29mX3RodW1ibmFpbEIRCg9vbmVfb2ZfaXNfdHJhc2g=');
+final $typed_data.Uint8List updateViewRequestDescriptor = $convert.base64Decode('ChFVcGRhdGVWaWV3UmVxdWVzdBIXCgd2aWV3X2lkGAEgASgJUgZ2aWV3SWQSFAoEbmFtZRgCIAEoCUgAUgRuYW1lEhQKBGRlc2MYAyABKAlIAVIEZGVzYxIeCgl0aHVtYm5haWwYBCABKAlIAlIJdGh1bWJuYWlsQg0KC29uZV9vZl9uYW1lQg0KC29uZV9vZl9kZXNjQhIKEG9uZV9vZl90aHVtYm5haWw=');
 @$core.Deprecated('Use updateViewParamsDescriptor instead')
 const UpdateViewParams$json = const {
   '1': 'UpdateViewParams',
@@ -36,15 +34,13 @@ const UpdateViewParams$json = const {
     const {'1': 'name', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'name'},
     const {'1': 'desc', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'desc'},
     const {'1': 'thumbnail', '3': 4, '4': 1, '5': 9, '9': 2, '10': 'thumbnail'},
-    const {'1': 'is_trash', '3': 5, '4': 1, '5': 8, '9': 3, '10': 'isTrash'},
   ],
   '8': const [
     const {'1': 'one_of_name'},
     const {'1': 'one_of_desc'},
     const {'1': 'one_of_thumbnail'},
-    const {'1': 'one_of_is_trash'},
   ],
 };
 
 /// Descriptor for `UpdateViewParams`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List updateViewParamsDescriptor = $convert.base64Decode('ChBVcGRhdGVWaWV3UGFyYW1zEhcKB3ZpZXdfaWQYASABKAlSBnZpZXdJZBIUCgRuYW1lGAIgASgJSABSBG5hbWUSFAoEZGVzYxgDIAEoCUgBUgRkZXNjEh4KCXRodW1ibmFpbBgEIAEoCUgCUgl0aHVtYm5haWwSGwoIaXNfdHJhc2gYBSABKAhIA1IHaXNUcmFzaEINCgtvbmVfb2ZfbmFtZUINCgtvbmVfb2ZfZGVzY0ISChBvbmVfb2ZfdGh1bWJuYWlsQhEKD29uZV9vZl9pc190cmFzaA==');
+final $typed_data.Uint8List updateViewParamsDescriptor = $convert.base64Decode('ChBVcGRhdGVWaWV3UGFyYW1zEhcKB3ZpZXdfaWQYASABKAlSBnZpZXdJZBIUCgRuYW1lGAIgASgJSABSBG5hbWUSFAoEZGVzYxgDIAEoCUgBUgRkZXNjEh4KCXRodW1ibmFpbBgEIAEoCUgCUgl0aHVtYm5haWxCDQoLb25lX29mX25hbWVCDQoLb25lX29mX2Rlc2NCEgoQb25lX29mX3RodW1ibmFpbA==');

+ 21 - 6
backend/src/service/trash/router.rs

@@ -14,10 +14,11 @@ use flowy_net::{
     response::FlowyResponse,
 };
 use flowy_workspace::{
-    entities::trash::parser::{TrashId, TrashIds, TrashTypeParser},
+    entities::trash::parser::{TrashId, TrashTypeParser},
     protobuf::{CreateTrashParams, TrashIdentifiers},
 };
 use sqlx::PgPool;
+use uuid::Uuid;
 
 pub async fn create_handler(
     payload: Payload,
@@ -30,9 +31,9 @@ pub async fn create_handler(
         .await
         .context("Failed to acquire a Postgres connection to create trash")?;
 
-    let trash_id = TrashId::parse(params.id).map_err(invalid_params)?;
+    let trash_id = check_trash_id(params.id)?;
     let ty = TrashTypeParser::parse(params.ty.value()).map_err(invalid_params)?;
-    let _ = create_trash(&mut transaction, trash_id.as_ref(), ty, logged_user).await?;
+    let _ = create_trash(&mut transaction, trash_id, ty, logged_user).await?;
 
     transaction
         .commit()
@@ -49,8 +50,8 @@ pub async fn delete_handler(payload: Payload, pool: Data<PgPool>) -> Result<Http
         .await
         .context("Failed to acquire a Postgres connection to delete trash")?;
 
-    let trash_ids = TrashIds::parse(params.ids.into_vec()).map_err(invalid_params)?;
-    let _ = delete_trash(&mut transaction, trash_ids.0).await?;
+    let trash_ids = check_trash_ids(params.ids.into_vec())?;
+    let _ = delete_trash(&mut transaction, trash_ids).await?;
     transaction
         .commit()
         .await
@@ -65,7 +66,7 @@ pub async fn read_handler(pool: Data<PgPool>, logged_user: LoggedUser) -> Result
         .await
         .context("Failed to acquire a Postgres connection to read trash")?;
 
-    let repeated_trash = read_trash(&mut transaction, logged_user).await?;
+    let repeated_trash = read_trash(&mut transaction, &logged_user).await?;
 
     transaction
         .commit()
@@ -74,3 +75,17 @@ pub async fn read_handler(pool: Data<PgPool>, logged_user: LoggedUser) -> Result
 
     Ok(FlowyResponse::success().pb(repeated_trash)?.into())
 }
+
+fn check_trash_id(id: String) -> Result<Uuid, ServerError> {
+    let trash_id = TrashId::parse(id).map_err(invalid_params)?;
+    let trash_id = Uuid::parse_str(trash_id.as_ref())?;
+    Ok(trash_id)
+}
+
+fn check_trash_ids(ids: Vec<String>) -> Result<Vec<Uuid>, ServerError> {
+    let mut trash_ids = vec![];
+    for id in ids {
+        trash_ids.push(check_trash_id(id)?)
+    }
+    Ok(trash_ids)
+}

+ 15 - 11
backend/src/service/trash/trash.rs

@@ -4,13 +4,14 @@ use crate::{
     sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
 };
 use ::protobuf::ProtobufEnum;
-use flowy_net::{errors::ServerError, response::FlowyResponse};
-use flowy_workspace::protobuf::{CreateTrashParams, RepeatedTrash, Trash, TrashIdentifiers, TrashType};
+use flowy_net::errors::ServerError;
+use flowy_workspace::protobuf::{RepeatedTrash, Trash, TrashIdentifiers, TrashType};
 use sqlx::{postgres::PgArguments, Postgres};
+use uuid::Uuid;
 
 pub(crate) async fn create_trash(
     transaction: &mut DBTransaction<'_>,
-    trash_id: &str,
+    trash_id: Uuid,
     ty: i32,
     user: LoggedUser,
 ) -> Result<(), ServerError> {
@@ -28,10 +29,7 @@ pub(crate) async fn create_trash(
     Ok(())
 }
 
-pub(crate) async fn delete_trash(
-    transaction: &mut DBTransaction<'_>,
-    trash_ids: Vec<String>,
-) -> Result<(), ServerError> {
+pub(crate) async fn delete_trash(transaction: &mut DBTransaction<'_>, trash_ids: Vec<Uuid>) -> Result<(), ServerError> {
     for trash_id in trash_ids {
         let (sql, args) = SqlBuilder::delete(TRASH_TABLE).and_where_eq("id", &trash_id).build()?;
         let _ = sqlx::query_with(&sql, args)
@@ -43,15 +41,21 @@ pub(crate) async fn delete_trash(
 }
 
 pub(crate) async fn read_trash_ids(
-    _user: &LoggedUser,
-    _transaction: &mut DBTransaction<'_>,
+    user: &LoggedUser,
+    transaction: &mut DBTransaction<'_>,
 ) -> Result<Vec<String>, ServerError> {
-    Ok(vec![])
+    let repeated_trash = read_trash(transaction, user).await?.take_items().into_vec();
+    let ids = repeated_trash
+        .into_iter()
+        .map(|trash| trash.id)
+        .collect::<Vec<String>>();
+
+    Ok(ids)
 }
 
 pub(crate) async fn read_trash(
     transaction: &mut DBTransaction<'_>,
-    user: LoggedUser,
+    user: &LoggedUser,
 ) -> Result<RepeatedTrash, ServerError> {
     let (sql, args) = SqlBuilder::select(TRASH_TABLE)
         .add_field("*")

+ 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, QueryViewParams, UpdateViewParams},
+    protobuf::{CreateViewParams, DeleteViewParams, UpdateViewParams, ViewIdentifier},
 };
 use sqlx::PgPool;
 use std::sync::Arc;
@@ -42,7 +42,7 @@ pub async fn create_handler(
 }
 
 pub async fn read_handler(payload: Payload, pool: Data<PgPool>, user: LoggedUser) -> Result<HttpResponse, ServerError> {
-    let params: QueryViewParams = parse_from_payload(payload).await?;
+    let params: ViewIdentifier = parse_from_payload(payload).await?;
     let view_id = check_view_ids(vec![params.view_id])?.pop().unwrap();
     let mut transaction = pool
         .begin()

+ 6 - 0
backend/src/service/view/view.rs

@@ -92,6 +92,12 @@ pub(crate) async fn read_view(
     transaction: &mut DBTransaction<'_>,
 ) -> Result<View, ServerError> {
     let table = read_view_table(view_id, transaction as &mut DBTransaction<'_>).await?;
+
+    let read_trash_ids = read_trash_ids(user, transaction).await?;
+    if read_trash_ids.contains(&table.id.to_string()) {
+        return Err(ServerError::record_not_found());
+    }
+
     let mut views = RepeatedView::default();
     views.set_items(
         read_view_belong_to_id(&user, transaction, &table.id.to_string())

+ 37 - 33
backend/tests/api/workspace.rs

@@ -1,7 +1,8 @@
 use crate::helper::*;
 use flowy_workspace::entities::{
     app::{AppIdentifier, DeleteAppParams, UpdateAppParams},
-    view::UpdateViewParams,
+    trash::{CreateTrashParams, TrashType},
+    view::{UpdateViewParams, ViewIdentifier},
     workspace::{CreateWorkspaceParams, DeleteWorkspaceParams, QueryWorkspaceParams, UpdateWorkspaceParams},
 };
 
@@ -97,9 +98,11 @@ async fn app_read_with_belongs_in_trash() {
     let _ = create_test_view(&test.server, &test.app.id).await;
     let view = create_test_view(&test.server, &test.app.id).await;
 
-    let update_params = UpdateViewParams::new(&view.id).trash();
-    // create trash
-    test.server.update_view(update_params).await;
+    let params = CreateTrashParams {
+        id: view.id.clone(),
+        ty: TrashType::View,
+    };
+    test.server.create_trash(params).await;
 
     let read_params = AppIdentifier::new(&test.app.id);
     let app = test.server.read_app(read_params).await.unwrap();
@@ -138,35 +141,36 @@ async fn view_create() {
     log::info!("{:?}", test.view);
 }
 
-// #[actix_rt::test]
-// async fn view_update() {
-//     let test = ViewTest::new().await;
-//     let new_name = "name view name";
-//
-//     // update
-//     let update_params =
-// UpdateViewParams::new(&test.view.id).trash().name(new_name);     test.server.
-// update_view(update_params).await;
-//
-//     // read
-//     let read_params = QueryViewParams::new(&test.view.id).trash();
-//     let view = test.server.read_view(read_params).await.unwrap();
-//     assert_eq!(&view.name, new_name);
-// }
-
-// #[actix_rt::test]
-// async fn view_delete() {
-//     let test = ViewTest::new().await;
-//     // delete
-//     let delete_params = DeleteViewParams {
-//         view_ids: vec![test.view.id.clone()],
-//     };
-//     test.server.delete_view(delete_params).await;
-//
-//     // read
-//     let read_params = QueryViewParams::new(&test.view.id).trash();
-//     assert_eq!(test.server.read_view(read_params).await.is_none(), true);
-// }
+#[actix_rt::test]
+async fn view_update() {
+    let test = ViewTest::new().await;
+    let new_name = "name view name";
+
+    // update
+    let update_params = UpdateViewParams::new(&test.view.id).name(new_name);
+    test.server.update_view(update_params).await;
+
+    // read
+    let read_params = ViewIdentifier::new(&test.view.id);
+    let view = test.server.read_view(read_params).await.unwrap();
+    assert_eq!(&view.name, new_name);
+}
+
+#[actix_rt::test]
+async fn view_delete() {
+    let test = ViewTest::new().await;
+
+    // delete
+    let params = CreateTrashParams {
+        id: test.view.id.clone(),
+        ty: TrashType::View,
+    };
+    test.server.create_trash(params).await;
+
+    // read
+    let read_params = ViewIdentifier::new(&test.view.id);
+    assert_eq!(test.server.read_view(read_params).await.is_none(), true);
+}
 
 #[actix_rt::test]
 async fn workspace_list_read() {

+ 1 - 1
backend/tests/helper.rs

@@ -106,7 +106,7 @@ impl TestUserServer {
         view
     }
 
-    pub async fn read_view(&self, params: QueryViewParams) -> Option<View> {
+    pub async fn read_view(&self, params: ViewIdentifier) -> Option<View> {
         let url = format!("{}/api/view", self.http_addr());
         let view = read_view_request(self.user_token(), params, &url).await.unwrap();
         view

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

@@ -48,7 +48,7 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         | "DeleteViewRequest"
         | "DeleteViewParams"
         | "QueryViewRequest"
-        | "QueryViewParams"
+        | "ViewIdentifier"
         | "OpenViewRequest"
         | "CreateViewRequest"
         | "CreateViewParams"

+ 1 - 0
rust-lib/flowy-net/src/errors.rs

@@ -36,6 +36,7 @@ impl ServerError {
     static_error!(connect_close, ErrorCode::ConnectClose);
     static_error!(connect_cancel, ErrorCode::ConnectCancel);
     static_error!(connect_refused, ErrorCode::ConnectRefused);
+    static_error!(record_not_found, ErrorCode::RecordNotFound);
 
     pub fn new(msg: String, code: ErrorCode) -> Self { Self { code, msg } }
 

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

@@ -77,15 +77,11 @@ impl ViewTest {
         }
     }
 
-    pub async fn move_view_to_trash(&self) {
-        let request = UpdateViewRequest {
-            view_id: self.view.id.clone(),
-            name: None,
-            desc: None,
-            thumbnail: None,
-            is_trash: Some(true),
+    pub async fn delete(&self) {
+        let request = DeleteViewRequest {
+            view_ids: vec![self.view.id.clone()],
         };
-        update_view(&self.sdk, request).await;
+        delete_view(&self.sdk, request).await;
     }
 }
 
@@ -229,6 +225,14 @@ pub async fn read_view(sdk: &FlowyTestSDK, request: QueryViewRequest) -> View {
         .parse::<View>()
 }
 
+pub async fn delete_view(sdk: &FlowyTestSDK, request: DeleteViewRequest) {
+    FlowyWorkspaceTest::new(sdk.clone())
+        .event(DeleteView)
+        .request(request)
+        .async_send()
+        .await;
+}
+
 pub async fn open_view(sdk: &FlowyTestSDK, request: OpenViewRequest) -> Doc {
     FlowyWorkspaceTest::new(sdk.clone())
         .event(OpenView)

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

@@ -37,6 +37,8 @@ bincode = { version = "1.3"}
 unicode-segmentation = "1.7.1"
 tracing = { version = "0.1", features = ["log"] }
 bytes = { version = "1.0" }
+crossbeam = "0.8.1"
+crossbeam-utils = "0.8"
 [dev-dependencies]
 flowy-test = { path = "../flowy-test" }
 serial_test = "0.5.1"

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

@@ -6,7 +6,7 @@ use std::convert::TryInto;
 #[derive(Default, ProtoBuf)]
 pub struct DeleteViewRequest {
     #[pb(index = 1)]
-    view_ids: Vec<String>,
+    pub view_ids: Vec<String>,
 }
 
 #[derive(Default, ProtoBuf)]

+ 10 - 6
rust-lib/flowy-workspace/src/entities/view/view_query.rs

@@ -18,12 +18,12 @@ impl QueryViewRequest {
 }
 
 #[derive(Default, ProtoBuf, Clone, Debug)]
-pub struct QueryViewParams {
+pub struct ViewIdentifier {
     #[pb(index = 1)]
     pub view_id: String,
 }
 
-impl QueryViewParams {
+impl ViewIdentifier {
     pub fn new(view_id: &str) -> Self {
         Self {
             view_id: view_id.to_owned(),
@@ -31,18 +31,22 @@ impl QueryViewParams {
     }
 }
 
-impl std::convert::Into<DocIdentifier> for QueryViewParams {
+impl std::convert::From<String> for ViewIdentifier {
+    fn from(view_id: String) -> Self { ViewIdentifier { view_id } }
+}
+
+impl std::convert::Into<DocIdentifier> for ViewIdentifier {
     fn into(self) -> DocIdentifier { DocIdentifier { doc_id: self.view_id } }
 }
 
-impl TryInto<QueryViewParams> for QueryViewRequest {
+impl TryInto<ViewIdentifier> for QueryViewRequest {
     type Error = WorkspaceError;
-    fn try_into(self) -> Result<QueryViewParams, Self::Error> {
+    fn try_into(self) -> Result<ViewIdentifier, Self::Error> {
         let view_id = ViewId::parse(self.view_id)
             .map_err(|e| WorkspaceError::view_id().context(e))?
             .0;
 
-        Ok(QueryViewParams { view_id })
+        Ok(ViewIdentifier { view_id })
     }
 }
 

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

@@ -19,9 +19,6 @@ pub struct UpdateViewRequest {
 
     #[pb(index = 4, one_of)]
     pub thumbnail: Option<String>,
-
-    #[pb(index = 5, one_of)]
-    pub is_trash: Option<bool>,
 }
 
 #[derive(Default, ProtoBuf, Clone, Debug)]
@@ -37,9 +34,6 @@ pub struct UpdateViewParams {
 
     #[pb(index = 4, one_of)]
     pub thumbnail: Option<String>,
-
-    #[pb(index = 5, one_of)]
-    pub is_trash: Option<bool>,
 }
 
 impl UpdateViewParams {
@@ -50,11 +44,6 @@ impl UpdateViewParams {
         }
     }
 
-    pub fn trash(mut self) -> Self {
-        self.is_trash = Some(true);
-        self
-    }
-
     pub fn name(mut self, name: &str) -> Self {
         self.name = Some(name.to_owned());
         self
@@ -106,7 +95,6 @@ impl TryInto<UpdateViewParams> for UpdateViewRequest {
             name,
             desc,
             thumbnail,
-            is_trash: self.is_trash,
         })
     }
 }

+ 25 - 15
rust-lib/flowy-workspace/src/handlers/view_handler.rs

@@ -1,18 +1,21 @@
 use crate::{
-    entities::view::{
-        CreateViewParams,
-        CreateViewRequest,
-        DeleteViewParams,
-        DeleteViewRequest,
-        OpenViewRequest,
-        QueryViewParams,
-        QueryViewRequest,
-        UpdateViewParams,
-        UpdateViewRequest,
-        View,
+    entities::{
+        trash::Trash,
+        view::{
+            CreateViewParams,
+            CreateViewRequest,
+            DeleteViewParams,
+            DeleteViewRequest,
+            OpenViewRequest,
+            QueryViewRequest,
+            UpdateViewParams,
+            UpdateViewRequest,
+            View,
+            ViewIdentifier,
+        },
     },
     errors::WorkspaceError,
-    services::ViewController,
+    services::{TrashCan, ViewController},
 };
 use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit};
 use flowy_document::entities::doc::{DocDelta, DocIdentifier};
@@ -33,7 +36,7 @@ pub(crate) async fn read_view_handler(
     data: Data<QueryViewRequest>,
     controller: Unit<Arc<ViewController>>,
 ) -> DataResult<View, WorkspaceError> {
-    let params: QueryViewParams = data.into_inner().try_into()?;
+    let params: ViewIdentifier = data.into_inner().try_into()?;
     let mut view = controller.read_view(params.clone()).await?;
     view.belongings = controller.read_views_belong_to(&params.view_id).await?;
 
@@ -61,13 +64,20 @@ pub(crate) async fn apply_doc_delta_handler(
     data_result(doc)
 }
 
-#[tracing::instrument(skip(data, controller), err)]
+#[tracing::instrument(skip(data, controller, trash_can), err)]
 pub(crate) async fn delete_view_handler(
     data: Data<DeleteViewRequest>,
     controller: Unit<Arc<ViewController>>,
+    trash_can: Unit<Arc<TrashCan>>,
 ) -> Result<(), WorkspaceError> {
     let params: DeleteViewParams = data.into_inner().try_into()?;
-    let _ = controller.delete_view(params).await?;
+    let trash = controller
+        .read_view_tables(params.view_ids)?
+        .into_iter()
+        .map(|view_table| view_table.into())
+        .collect::<Vec<Trash>>();
+
+    let _ = trash_can.add(trash).await?;
     Ok(())
 }
 

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

@@ -183,7 +183,7 @@ impl ::protobuf::reflect::ProtobufValue for QueryViewRequest {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct QueryViewParams {
+pub struct ViewIdentifier {
     // message fields
     pub view_id: ::std::string::String,
     // special fields
@@ -191,14 +191,14 @@ pub struct QueryViewParams {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a QueryViewParams {
-    fn default() -> &'a QueryViewParams {
-        <QueryViewParams as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a ViewIdentifier {
+    fn default() -> &'a ViewIdentifier {
+        <ViewIdentifier as ::protobuf::Message>::default_instance()
     }
 }
 
-impl QueryViewParams {
-    pub fn new() -> QueryViewParams {
+impl ViewIdentifier {
+    pub fn new() -> ViewIdentifier {
         ::std::default::Default::default()
     }
 
@@ -229,7 +229,7 @@ impl QueryViewParams {
     }
 }
 
-impl ::protobuf::Message for QueryViewParams {
+impl ::protobuf::Message for ViewIdentifier {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -295,8 +295,8 @@ impl ::protobuf::Message for QueryViewParams {
         Self::descriptor_static()
     }
 
-    fn new() -> QueryViewParams {
-        QueryViewParams::new()
+    fn new() -> ViewIdentifier {
+        ViewIdentifier::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -305,37 +305,37 @@ impl ::protobuf::Message for QueryViewParams {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "view_id",
-                |m: &QueryViewParams| { &m.view_id },
-                |m: &mut QueryViewParams| { &mut m.view_id },
+                |m: &ViewIdentifier| { &m.view_id },
+                |m: &mut ViewIdentifier| { &mut m.view_id },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<QueryViewParams>(
-                "QueryViewParams",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<ViewIdentifier>(
+                "ViewIdentifier",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static QueryViewParams {
-        static instance: ::protobuf::rt::LazyV2<QueryViewParams> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(QueryViewParams::new)
+    fn default_instance() -> &'static ViewIdentifier {
+        static instance: ::protobuf::rt::LazyV2<ViewIdentifier> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(ViewIdentifier::new)
     }
 }
 
-impl ::protobuf::Clear for QueryViewParams {
+impl ::protobuf::Clear for ViewIdentifier {
     fn clear(&mut self) {
         self.view_id.clear();
         self.unknown_fields.clear();
     }
 }
 
-impl ::std::fmt::Debug for QueryViewParams {
+impl ::std::fmt::Debug for ViewIdentifier {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for QueryViewParams {
+impl ::protobuf::reflect::ProtobufValue for ViewIdentifier {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -502,7 +502,7 @@ impl ::protobuf::reflect::ProtobufValue for OpenViewRequest {
 
 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\x0fQueryViewParams\x12\x17\n\x07view_id\
+    \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\
@@ -510,7 +510,7 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \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\x17\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x06\x04\x17\n\x0c\
+    \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\

+ 39 - 164
rust-lib/flowy-workspace/src/protobuf/model/view_update.rs

@@ -31,7 +31,6 @@ pub struct UpdateViewRequest {
     pub one_of_name: ::std::option::Option<UpdateViewRequest_oneof_one_of_name>,
     pub one_of_desc: ::std::option::Option<UpdateViewRequest_oneof_one_of_desc>,
     pub one_of_thumbnail: ::std::option::Option<UpdateViewRequest_oneof_one_of_thumbnail>,
-    pub one_of_is_trash: ::std::option::Option<UpdateViewRequest_oneof_one_of_is_trash>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -58,11 +57,6 @@ pub enum UpdateViewRequest_oneof_one_of_thumbnail {
     thumbnail(::std::string::String),
 }
 
-#[derive(Clone,PartialEq,Debug)]
-pub enum UpdateViewRequest_oneof_one_of_is_trash {
-    is_trash(bool),
-}
-
 impl UpdateViewRequest {
     pub fn new() -> UpdateViewRequest {
         ::std::default::Default::default()
@@ -240,31 +234,6 @@ impl UpdateViewRequest {
             ::std::string::String::new()
         }
     }
-
-    // bool is_trash = 5;
-
-
-    pub fn get_is_trash(&self) -> bool {
-        match self.one_of_is_trash {
-            ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_is_trash::is_trash(v)) => v,
-            _ => false,
-        }
-    }
-    pub fn clear_is_trash(&mut self) {
-        self.one_of_is_trash = ::std::option::Option::None;
-    }
-
-    pub fn has_is_trash(&self) -> bool {
-        match self.one_of_is_trash {
-            ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_is_trash::is_trash(..)) => true,
-            _ => false,
-        }
-    }
-
-    // Param is passed by value, moved
-    pub fn set_is_trash(&mut self, v: bool) {
-        self.one_of_is_trash = ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_is_trash::is_trash(v))
-    }
 }
 
 impl ::protobuf::Message for UpdateViewRequest {
@@ -297,12 +266,6 @@ impl ::protobuf::Message for UpdateViewRequest {
                     }
                     self.one_of_thumbnail = ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_thumbnail::thumbnail(is.read_string()?));
                 },
-                5 => {
-                    if wire_type != ::protobuf::wire_format::WireTypeVarint {
-                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
-                    }
-                    self.one_of_is_trash = ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_is_trash::is_trash(is.read_bool()?));
-                },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
                 },
@@ -339,13 +302,6 @@ impl ::protobuf::Message for UpdateViewRequest {
                 },
             };
         }
-        if let ::std::option::Option::Some(ref v) = self.one_of_is_trash {
-            match v {
-                &UpdateViewRequest_oneof_one_of_is_trash::is_trash(v) => {
-                    my_size += 2;
-                },
-            };
-        }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
         my_size
@@ -376,13 +332,6 @@ impl ::protobuf::Message for UpdateViewRequest {
                 },
             };
         }
-        if let ::std::option::Option::Some(ref v) = self.one_of_is_trash {
-            match v {
-                &UpdateViewRequest_oneof_one_of_is_trash::is_trash(v) => {
-                    os.write_bool(5, v)?;
-                },
-            };
-        }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
     }
@@ -441,11 +390,6 @@ impl ::protobuf::Message for UpdateViewRequest {
                 UpdateViewRequest::has_thumbnail,
                 UpdateViewRequest::get_thumbnail,
             ));
-            fields.push(::protobuf::reflect::accessor::make_singular_bool_accessor::<_>(
-                "is_trash",
-                UpdateViewRequest::has_is_trash,
-                UpdateViewRequest::get_is_trash,
-            ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<UpdateViewRequest>(
                 "UpdateViewRequest",
                 fields,
@@ -466,7 +410,6 @@ impl ::protobuf::Clear for UpdateViewRequest {
         self.one_of_name = ::std::option::Option::None;
         self.one_of_desc = ::std::option::Option::None;
         self.one_of_thumbnail = ::std::option::Option::None;
-        self.one_of_is_trash = ::std::option::Option::None;
         self.unknown_fields.clear();
     }
 }
@@ -491,7 +434,6 @@ pub struct UpdateViewParams {
     pub one_of_name: ::std::option::Option<UpdateViewParams_oneof_one_of_name>,
     pub one_of_desc: ::std::option::Option<UpdateViewParams_oneof_one_of_desc>,
     pub one_of_thumbnail: ::std::option::Option<UpdateViewParams_oneof_one_of_thumbnail>,
-    pub one_of_is_trash: ::std::option::Option<UpdateViewParams_oneof_one_of_is_trash>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -518,11 +460,6 @@ pub enum UpdateViewParams_oneof_one_of_thumbnail {
     thumbnail(::std::string::String),
 }
 
-#[derive(Clone,PartialEq,Debug)]
-pub enum UpdateViewParams_oneof_one_of_is_trash {
-    is_trash(bool),
-}
-
 impl UpdateViewParams {
     pub fn new() -> UpdateViewParams {
         ::std::default::Default::default()
@@ -700,31 +637,6 @@ impl UpdateViewParams {
             ::std::string::String::new()
         }
     }
-
-    // bool is_trash = 5;
-
-
-    pub fn get_is_trash(&self) -> bool {
-        match self.one_of_is_trash {
-            ::std::option::Option::Some(UpdateViewParams_oneof_one_of_is_trash::is_trash(v)) => v,
-            _ => false,
-        }
-    }
-    pub fn clear_is_trash(&mut self) {
-        self.one_of_is_trash = ::std::option::Option::None;
-    }
-
-    pub fn has_is_trash(&self) -> bool {
-        match self.one_of_is_trash {
-            ::std::option::Option::Some(UpdateViewParams_oneof_one_of_is_trash::is_trash(..)) => true,
-            _ => false,
-        }
-    }
-
-    // Param is passed by value, moved
-    pub fn set_is_trash(&mut self, v: bool) {
-        self.one_of_is_trash = ::std::option::Option::Some(UpdateViewParams_oneof_one_of_is_trash::is_trash(v))
-    }
 }
 
 impl ::protobuf::Message for UpdateViewParams {
@@ -757,12 +669,6 @@ impl ::protobuf::Message for UpdateViewParams {
                     }
                     self.one_of_thumbnail = ::std::option::Option::Some(UpdateViewParams_oneof_one_of_thumbnail::thumbnail(is.read_string()?));
                 },
-                5 => {
-                    if wire_type != ::protobuf::wire_format::WireTypeVarint {
-                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
-                    }
-                    self.one_of_is_trash = ::std::option::Option::Some(UpdateViewParams_oneof_one_of_is_trash::is_trash(is.read_bool()?));
-                },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
                 },
@@ -799,13 +705,6 @@ impl ::protobuf::Message for UpdateViewParams {
                 },
             };
         }
-        if let ::std::option::Option::Some(ref v) = self.one_of_is_trash {
-            match v {
-                &UpdateViewParams_oneof_one_of_is_trash::is_trash(v) => {
-                    my_size += 2;
-                },
-            };
-        }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
         my_size
@@ -836,13 +735,6 @@ impl ::protobuf::Message for UpdateViewParams {
                 },
             };
         }
-        if let ::std::option::Option::Some(ref v) = self.one_of_is_trash {
-            match v {
-                &UpdateViewParams_oneof_one_of_is_trash::is_trash(v) => {
-                    os.write_bool(5, v)?;
-                },
-            };
-        }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
     }
@@ -901,11 +793,6 @@ impl ::protobuf::Message for UpdateViewParams {
                 UpdateViewParams::has_thumbnail,
                 UpdateViewParams::get_thumbnail,
             ));
-            fields.push(::protobuf::reflect::accessor::make_singular_bool_accessor::<_>(
-                "is_trash",
-                UpdateViewParams::has_is_trash,
-                UpdateViewParams::get_is_trash,
-            ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<UpdateViewParams>(
                 "UpdateViewParams",
                 fields,
@@ -926,7 +813,6 @@ impl ::protobuf::Clear for UpdateViewParams {
         self.one_of_name = ::std::option::Option::None;
         self.one_of_desc = ::std::option::Option::None;
         self.one_of_thumbnail = ::std::option::Option::None;
-        self.one_of_is_trash = ::std::option::Option::None;
         self.unknown_fields.clear();
     }
 }
@@ -944,58 +830,47 @@ impl ::protobuf::reflect::ProtobufValue for UpdateViewParams {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x11view_update.proto\"\xda\x01\n\x11UpdateViewRequest\x12\x17\n\x07vi\
+    \n\x11view_update.proto\"\xaa\x01\n\x11UpdateViewRequest\x12\x17\n\x07vi\
     ew_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\n\x04name\x18\x02\x20\x01(\t\
     H\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\x01(\tH\x01R\x04desc\x12\x1e\
-    \n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthumbnail\x12\x1b\n\x08is_trash\
-    \x18\x05\x20\x01(\x08H\x03R\x07isTrashB\r\n\x0bone_of_nameB\r\n\x0bone_o\
-    f_descB\x12\n\x10one_of_thumbnailB\x11\n\x0fone_of_is_trash\"\xd9\x01\n\
-    \x10UpdateViewParams\x12\x17\n\x07view_id\x18\x01\x20\x01(\tR\x06viewId\
-    \x12\x14\n\x04name\x18\x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\
-    \x03\x20\x01(\tH\x01R\x04desc\x12\x1e\n\tthumbnail\x18\x04\x20\x01(\tH\
-    \x02R\tthumbnail\x12\x1b\n\x08is_trash\x18\x05\x20\x01(\x08H\x03R\x07isT\
-    rashB\r\n\x0bone_of_nameB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnailB\
-    \x11\n\x0fone_of_is_trashJ\xc0\x06\n\x06\x12\x04\0\0\x0f\x01\n\x08\n\x01\
-    \x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x08\x01\n\n\n\x03\x04\
-    \0\x01\x12\x03\x02\x08\x19\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\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\
-    \x0b\n\x04\x04\0\x08\0\x12\x03\x04\x04*\n\x0c\n\x05\x04\0\x08\0\x01\x12\
-    \x03\x04\n\x15\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x18(\n\x0c\n\x05\
-    \x04\0\x02\x01\x05\x12\x03\x04\x18\x1e\n\x0c\n\x05\x04\0\x02\x01\x01\x12\
-    \x03\x04\x1f#\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04&'\n\x0b\n\x04\
-    \x04\0\x08\x01\x12\x03\x05\x04*\n\x0c\n\x05\x04\0\x08\x01\x01\x12\x03\
-    \x05\n\x15\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x18(\n\x0c\n\x05\x04\0\
-    \x02\x02\x05\x12\x03\x05\x18\x1e\n\x0c\n\x05\x04\0\x02\x02\x01\x12\x03\
-    \x05\x1f#\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05&'\n\x0b\n\x04\x04\0\
-    \x08\x02\x12\x03\x06\x044\n\x0c\n\x05\x04\0\x08\x02\x01\x12\x03\x06\n\
-    \x1a\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x1d2\n\x0c\n\x05\x04\0\x02\
-    \x03\x05\x12\x03\x06\x1d#\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06$-\n\
-    \x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x0601\n\x0b\n\x04\x04\0\x08\x03\x12\
-    \x03\x07\x040\n\x0c\n\x05\x04\0\x08\x03\x01\x12\x03\x07\n\x19\n\x0b\n\
-    \x04\x04\0\x02\x04\x12\x03\x07\x1c.\n\x0c\n\x05\x04\0\x02\x04\x05\x12\
-    \x03\x07\x1c\x20\n\x0c\n\x05\x04\0\x02\x04\x01\x12\x03\x07!)\n\x0c\n\x05\
-    \x04\0\x02\x04\x03\x12\x03\x07,-\n\n\n\x02\x04\x01\x12\x04\t\0\x0f\x01\n\
-    \n\n\x03\x04\x01\x01\x12\x03\t\x08\x18\n\x0b\n\x04\x04\x01\x02\0\x12\x03\
-    \n\x04\x17\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\n\x04\n\n\x0c\n\x05\x04\
-    \x01\x02\0\x01\x12\x03\n\x0b\x12\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\n\
-    \x15\x16\n\x0b\n\x04\x04\x01\x08\0\x12\x03\x0b\x04*\n\x0c\n\x05\x04\x01\
-    \x08\0\x01\x12\x03\x0b\n\x15\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x0b\x18\
-    (\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x0b\x18\x1e\n\x0c\n\x05\x04\
-    \x01\x02\x01\x01\x12\x03\x0b\x1f#\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\
-    \x03\x0b&'\n\x0b\n\x04\x04\x01\x08\x01\x12\x03\x0c\x04*\n\x0c\n\x05\x04\
-    \x01\x08\x01\x01\x12\x03\x0c\n\x15\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\
-    \x0c\x18(\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\x0c\x18\x1e\n\x0c\n\
-    \x05\x04\x01\x02\x02\x01\x12\x03\x0c\x1f#\n\x0c\n\x05\x04\x01\x02\x02\
-    \x03\x12\x03\x0c&'\n\x0b\n\x04\x04\x01\x08\x02\x12\x03\r\x044\n\x0c\n\
-    \x05\x04\x01\x08\x02\x01\x12\x03\r\n\x1a\n\x0b\n\x04\x04\x01\x02\x03\x12\
-    \x03\r\x1d2\n\x0c\n\x05\x04\x01\x02\x03\x05\x12\x03\r\x1d#\n\x0c\n\x05\
-    \x04\x01\x02\x03\x01\x12\x03\r$-\n\x0c\n\x05\x04\x01\x02\x03\x03\x12\x03\
-    \r01\n\x0b\n\x04\x04\x01\x08\x03\x12\x03\x0e\x040\n\x0c\n\x05\x04\x01\
-    \x08\x03\x01\x12\x03\x0e\n\x19\n\x0b\n\x04\x04\x01\x02\x04\x12\x03\x0e\
-    \x1c.\n\x0c\n\x05\x04\x01\x02\x04\x05\x12\x03\x0e\x1c\x20\n\x0c\n\x05\
-    \x04\x01\x02\x04\x01\x12\x03\x0e!)\n\x0c\n\x05\x04\x01\x02\x04\x03\x12\
-    \x03\x0e,-b\x06proto3\
+    \n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthumbnailB\r\n\x0bone_of_nameB\r\
+    \n\x0bone_of_descB\x12\n\x10one_of_thumbnail\"\xa9\x01\n\x10UpdateViewPa\
+    rams\x12\x17\n\x07view_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\n\x04nam\
+    e\x18\x02\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\x01(\tH\
+    \x01R\x04desc\x12\x1e\n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthumbnailB\
+    \r\n\x0bone_of_nameB\r\n\x0bone_of_descB\x12\n\x10one_of_thumbnailJ\x9c\
+    \x05\n\x06\x12\x04\0\0\r\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
+    \x04\0\x12\x04\x02\0\x07\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\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\x0b\n\x04\x04\0\x08\0\x12\
+    \x03\x04\x04*\n\x0c\n\x05\x04\0\x08\0\x01\x12\x03\x04\n\x15\n\x0b\n\x04\
+    \x04\0\x02\x01\x12\x03\x04\x18(\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\
+    \x04\x18\x1e\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x1f#\n\x0c\n\x05\
+    \x04\0\x02\x01\x03\x12\x03\x04&'\n\x0b\n\x04\x04\0\x08\x01\x12\x03\x05\
+    \x04*\n\x0c\n\x05\x04\0\x08\x01\x01\x12\x03\x05\n\x15\n\x0b\n\x04\x04\0\
+    \x02\x02\x12\x03\x05\x18(\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x18\
+    \x1e\n\x0c\n\x05\x04\0\x02\x02\x01\x12\x03\x05\x1f#\n\x0c\n\x05\x04\0\
+    \x02\x02\x03\x12\x03\x05&'\n\x0b\n\x04\x04\0\x08\x02\x12\x03\x06\x044\n\
+    \x0c\n\x05\x04\0\x08\x02\x01\x12\x03\x06\n\x1a\n\x0b\n\x04\x04\0\x02\x03\
+    \x12\x03\x06\x1d2\n\x0c\n\x05\x04\0\x02\x03\x05\x12\x03\x06\x1d#\n\x0c\n\
+    \x05\x04\0\x02\x03\x01\x12\x03\x06$-\n\x0c\n\x05\x04\0\x02\x03\x03\x12\
+    \x03\x0601\n\n\n\x02\x04\x01\x12\x04\x08\0\r\x01\n\n\n\x03\x04\x01\x01\
+    \x12\x03\x08\x08\x18\n\x0b\n\x04\x04\x01\x02\0\x12\x03\t\x04\x17\n\x0c\n\
+    \x05\x04\x01\x02\0\x05\x12\x03\t\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\
+    \x03\t\x0b\x12\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\t\x15\x16\n\x0b\n\
+    \x04\x04\x01\x08\0\x12\x03\n\x04*\n\x0c\n\x05\x04\x01\x08\0\x01\x12\x03\
+    \n\n\x15\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\n\x18(\n\x0c\n\x05\x04\x01\
+    \x02\x01\x05\x12\x03\n\x18\x1e\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\n\
+    \x1f#\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\n&'\n\x0b\n\x04\x04\x01\
+    \x08\x01\x12\x03\x0b\x04*\n\x0c\n\x05\x04\x01\x08\x01\x01\x12\x03\x0b\n\
+    \x15\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\x0b\x18(\n\x0c\n\x05\x04\x01\
+    \x02\x02\x05\x12\x03\x0b\x18\x1e\n\x0c\n\x05\x04\x01\x02\x02\x01\x12\x03\
+    \x0b\x1f#\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\x0b&'\n\x0b\n\x04\x04\
+    \x01\x08\x02\x12\x03\x0c\x044\n\x0c\n\x05\x04\x01\x08\x02\x01\x12\x03\
+    \x0c\n\x1a\n\x0b\n\x04\x04\x01\x02\x03\x12\x03\x0c\x1d2\n\x0c\n\x05\x04\
+    \x01\x02\x03\x05\x12\x03\x0c\x1d#\n\x0c\n\x05\x04\x01\x02\x03\x01\x12\
+    \x03\x0c$-\n\x0c\n\x05\x04\x01\x02\x03\x03\x12\x03\x0c01b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -3,7 +3,7 @@ syntax = "proto3";
 message QueryViewRequest {
     string view_id = 1;
 }
-message QueryViewParams {
+message ViewIdentifier {
     string view_id = 1;
 }
 message OpenViewRequest {

+ 0 - 2
rust-lib/flowy-workspace/src/protobuf/proto/view_update.proto

@@ -5,12 +5,10 @@ message UpdateViewRequest {
     oneof one_of_name { string name = 2; };
     oneof one_of_desc { string desc = 3; };
     oneof one_of_thumbnail { string thumbnail = 4; };
-    oneof one_of_is_trash { bool is_trash = 5; };
 }
 message UpdateViewParams {
     string view_id = 1;
     oneof one_of_name { string name = 2; };
     oneof one_of_desc { string desc = 3; };
     oneof one_of_thumbnail { string thumbnail = 4; };
-    oneof one_of_is_trash { bool is_trash = 5; };
 }

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

@@ -9,7 +9,7 @@ pub use server_api_mock::*;
 use crate::{
     entities::{
         app::{App, AppIdentifier, CreateAppParams, DeleteAppParams, UpdateAppParams},
-        view::{CreateViewParams, DeleteViewParams, QueryViewParams, UpdateViewParams, View},
+        view::{CreateViewParams, DeleteViewParams, UpdateViewParams, View, ViewIdentifier},
         workspace::{
             CreateWorkspaceParams,
             DeleteWorkspaceParams,
@@ -44,7 +44,7 @@ pub trait WorkspaceServerAPI {
     // View
     fn create_view(&self, token: &str, params: CreateViewParams) -> ResultFuture<View, WorkspaceError>;
 
-    fn read_view(&self, token: &str, params: QueryViewParams) -> ResultFuture<Option<View>, WorkspaceError>;
+    fn read_view(&self, token: &str, params: ViewIdentifier) -> ResultFuture<Option<View>, WorkspaceError>;
 
     fn delete_view(&self, token: &str, params: DeleteViewParams) -> ResultFuture<(), WorkspaceError>;
 

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

@@ -2,7 +2,7 @@ use crate::{
     entities::{
         app::{App, AppIdentifier, CreateAppParams, DeleteAppParams, UpdateAppParams},
         trash::CreateTrashParams,
-        view::{CreateViewParams, DeleteViewParams, QueryViewParams, UpdateViewParams, View},
+        view::{CreateViewParams, DeleteViewParams, UpdateViewParams, View, ViewIdentifier},
         workspace::{
             CreateWorkspaceParams,
             DeleteWorkspaceParams,
@@ -63,7 +63,7 @@ impl WorkspaceServerAPI for WorkspaceServer {
         ResultFuture::new(async move { create_view_request(&token, params, &url).await })
     }
 
-    fn read_view(&self, token: &str, params: QueryViewParams) -> ResultFuture<Option<View>, WorkspaceError> {
+    fn read_view(&self, token: &str, params: ViewIdentifier) -> ResultFuture<Option<View>, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.view_url();
         ResultFuture::new(async move { read_view_request(&token, params, &url).await })
@@ -219,11 +219,7 @@ pub async fn create_view_request(token: &str, params: CreateViewParams, url: &st
     Ok(view)
 }
 
-pub async fn read_view_request(
-    token: &str,
-    params: QueryViewParams,
-    url: &str,
-) -> Result<Option<View>, WorkspaceError> {
+pub async fn read_view_request(token: &str, params: ViewIdentifier, url: &str) -> Result<Option<View>, WorkspaceError> {
     let view = request_builder()
         .get(&url.to_owned())
         .header(HEADER_TOKEN, token)

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

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

+ 39 - 20
rust-lib/flowy-workspace/src/services/trash_can.rs

@@ -3,14 +3,16 @@ use crate::{
     errors::{WorkspaceError, WorkspaceResult},
     module::WorkspaceDatabase,
     notify::{send_anonymous_dart_notification, WorkspaceNotification},
-    sql_tables::trash::{TrashTable, TrashTableSql},
+    sql_tables::trash::TrashTableSql,
 };
+use crossbeam_utils::thread;
 use flowy_database::SqliteConnection;
 use std::sync::Arc;
 use tokio::sync::{broadcast, mpsc};
 
 #[derive(Clone)]
 pub enum TrashEvent {
+    NewTrash(TrashType, Vec<String>, mpsc::Sender<WorkspaceResult<()>>),
     Putback(TrashType, Vec<String>, mpsc::Sender<WorkspaceResult<()>>),
     Delete(TrashType, Vec<String>, mpsc::Sender<WorkspaceResult<()>>),
 }
@@ -28,6 +30,11 @@ impl TrashEvent {
                     return Some(self);
                 }
             },
+            TrashEvent::NewTrash(source, _, _) => {
+                if source == &s {
+                    return Some(self);
+                }
+            },
         }
         None
     }
@@ -98,25 +105,37 @@ impl TrashCan {
     // DELETE operations. It’s not possible for us to use these commands to
     // CREATE and DROP tables operations because those are auto-commit in the
     // database.
-    #[tracing::instrument(level = "debug", skip(self, trash, ty, conn), fields(add_trash)  err)]
-    pub fn add<T: Into<Trash>>(&self, trash: T, ty: TrashType, conn: &SqliteConnection) -> Result<(), WorkspaceError> {
-        let trash = trash.into();
-        let trash_table = TrashTable {
-            id: trash.id,
-            name: trash.name,
-            desc: "".to_owned(),
-            modified_time: trash.modified_time,
-            create_time: trash.create_time,
-            ty: ty.into(),
-        };
-
-        tracing::Span::current().record(
-            "add_trash",
-            &format!("{:?}: {}", &trash_table.ty, trash_table.id).as_str(),
-        );
-
-        let _ = TrashTableSql::create_trash(trash_table, &*conn)?;
-        let _ = self.notify_dart_trash_did_update(&conn)?;
+    #[tracing::instrument(level = "debug", skip(self, trash), err)]
+    pub async fn add<T: Into<Trash>>(&self, trash: Vec<T>) -> Result<(), WorkspaceError> {
+        let (tx, mut rx) = mpsc::channel::<WorkspaceResult<()>>(1);
+        let trash = trash.into_iter().map(|t| t.into()).collect::<Vec<Trash>>();
+        let mut ids = vec![];
+        let mut trash_type = None;
+        let _ = thread::scope(|_s| {
+            let conn = self.database.db_connection()?;
+            conn.immediate_transaction::<_, WorkspaceError, _>(|| {
+                for t in trash {
+                    if trash_type == None {
+                        trash_type = Some(t.ty.clone());
+                    }
+
+                    if trash_type.as_ref().unwrap() != &t.ty {
+                        return Err(WorkspaceError::internal());
+                    }
+
+                    ids.push(t.id.clone());
+                    let _ = TrashTableSql::create_trash(t.into(), &*conn)?;
+                }
+                Ok(())
+            })?;
+            Ok::<(), WorkspaceError>(())
+        })
+        .unwrap()?;
+
+        if let Some(trash_type) = trash_type {
+            let _ = self.notify.send(TrashEvent::NewTrash(trash_type, ids, tx));
+            let _ = rx.recv().await.unwrap()?;
+        }
 
         Ok(())
     }

+ 37 - 62
rust-lib/flowy-workspace/src/services/view_controller.rs

@@ -8,7 +8,7 @@ use crate::{
 };
 
 use crate::{
-    entities::view::{DeleteViewParams, QueryViewParams, RepeatedView},
+    entities::view::{DeleteViewParams, RepeatedView, ViewIdentifier},
     errors::internal_error,
     module::WorkspaceUser,
     notify::WorkspaceNotification,
@@ -80,7 +80,7 @@ impl ViewController {
         Ok(())
     }
 
-    pub(crate) async fn read_view(&self, params: QueryViewParams) -> Result<View, WorkspaceError> {
+    pub(crate) async fn read_view(&self, params: ViewIdentifier) -> Result<View, WorkspaceError> {
         let conn = self.database.db_connection()?;
         let view_table = ViewTableSql::read_view(&params.view_id, &*conn)?;
         let view: View = view_table.into();
@@ -88,32 +88,23 @@ impl ViewController {
         Ok(view)
     }
 
-    #[tracing::instrument(level = "debug", skip(self), err)]
-    pub(crate) async fn open_view(&self, params: DocIdentifier) -> Result<DocDelta, WorkspaceError> {
-        let edit_context = self.document.open(params, self.database.db_pool()?).await?;
-        Ok(edit_context.delta().await.map_err(internal_error)?)
-    }
-
-    #[tracing::instrument(level = "debug", skip(self, params), err)]
-    pub(crate) async fn delete_view(&self, params: DeleteViewParams) -> Result<(), WorkspaceError> {
+    pub(crate) fn read_view_tables(&self, ids: Vec<String>) -> Result<Vec<ViewTable>, WorkspaceError> {
         let conn = &*self.database.db_connection()?;
-        let _ = self.delete_view_on_server(params.view_ids.clone());
-
+        let mut view_tables = vec![];
         conn.immediate_transaction::<_, WorkspaceError, _>(|| {
-            for view_id in params.view_ids {
-                let view_table = ViewTableSql::delete_view(&view_id, conn)?;
-                let _ = self.document.delete(view_id.into())?;
-
-                let repeated_view = ViewTableSql::read_views(&view_table.belong_to_id, conn)?;
-
-                send_dart_notification(&view_table.belong_to_id, WorkspaceNotification::AppViewsChanged)
-                    .payload(repeated_view)
-                    .send();
+            for view_id in ids {
+                view_tables.push(ViewTableSql::read_view(&view_id, conn)?);
             }
             Ok(())
         })?;
 
-        Ok(())
+        Ok(view_tables)
+    }
+
+    #[tracing::instrument(level = "debug", skip(self), err)]
+    pub(crate) async fn open_view(&self, params: DocIdentifier) -> Result<DocDelta, WorkspaceError> {
+        let edit_context = self.document.open(params, self.database.db_pool()?).await?;
+        Ok(edit_context.delta().await.map_err(internal_error)?)
     }
 
     // belong_to_id will be the app_id or view_id.
@@ -136,20 +127,9 @@ impl ViewController {
             let view: View = ViewTableSql::read_view(&view_id, conn)?.into();
             Ok(view)
         })?;
-
-        match params.is_trash {
-            None => {
-                send_dart_notification(&view_id, WorkspaceNotification::ViewUpdated)
-                    .payload(updated_view.clone())
-                    .send();
-            },
-            Some(is_trash) => {
-                if is_trash {
-                    self.trash_can.add(updated_view.clone(), TrashType::View, conn)?;
-                }
-                let _ = notify_view_num_did_change(&updated_view.belong_to_id, conn)?;
-            },
-        }
+        send_dart_notification(&view_id, WorkspaceNotification::ViewUpdated)
+            .payload(updated_view.clone())
+            .send();
 
         let _ = self.update_view_on_server(params);
         Ok(updated_view)
@@ -203,7 +183,7 @@ impl ViewController {
     }
 
     #[tracing::instrument(skip(self), err)]
-    fn read_view_on_server(&self, params: QueryViewParams) -> Result<(), WorkspaceError> {
+    fn read_view_on_server(&self, params: ViewIdentifier) -> Result<(), WorkspaceError> {
         let token = self.user.token()?;
         let server = self.server.clone();
         spawn(async move {
@@ -221,6 +201,7 @@ impl ViewController {
     fn listen_trash_can_event(&self) {
         let mut rx = self.trash_can.subscribe();
         let database = self.database.clone();
+        let document = self.document.clone();
         let _ = tokio::spawn(async move {
             loop {
                 let mut stream = Box::pin(rx.recv().into_stream().filter_map(|result| async move {
@@ -231,7 +212,7 @@ impl ViewController {
                 }));
                 let event: Option<TrashEvent> = stream.next().await;
                 match event {
-                    Some(event) => handle_trash_event(database.clone(), event),
+                    Some(event) => handle_trash_event(database.clone(), document.clone(), event),
                     None => {},
                 }
             }
@@ -239,28 +220,16 @@ impl ViewController {
     }
 }
 
-fn notify_view_num_did_change(belong_to_id: &str, conn: &SqliteConnection) -> WorkspaceResult<()> {
-    let repeated_view = ViewTableSql::read_views(belong_to_id, conn)?;
-    send_dart_notification(belong_to_id, WorkspaceNotification::AppViewsChanged)
-        .payload(repeated_view)
-        .send();
-    Ok(())
-}
-
-fn handle_trash_event(database: Arc<dyn WorkspaceDatabase>, event: TrashEvent) {
+fn handle_trash_event(database: Arc<dyn WorkspaceDatabase>, document: Arc<FlowyDocument>, event: TrashEvent) {
     let db_result = database.db_connection();
+
     match event {
-        TrashEvent::Putback(_, putback_ids, ret) => {
+        TrashEvent::NewTrash(_, view_ids, ret) | TrashEvent::Putback(_, view_ids, ret) => {
             let result = || {
                 let conn = &*db_result?;
                 let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
-                    for putback_id in putback_ids {
-                        match ViewTableSql::read_view(&putback_id, conn) {
-                            Ok(view_table) => {
-                                let _ = notify_view_num_did_change(&view_table.belong_to_id, conn)?;
-                            },
-                            Err(e) => log::error!("Putback view: {} failed: {:?}", putback_id, e),
-                        }
+                    for view_id in view_ids {
+                        let _ = notify_view_num_did_change(&view_id, conn)?;
                     }
                     Ok(())
                 })?;
@@ -272,13 +241,10 @@ fn handle_trash_event(database: Arc<dyn WorkspaceDatabase>, event: TrashEvent) {
             let result = || {
                 let conn = &*db_result?;
                 let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
-                    for delete_id in delete_ids {
-                        match ViewTableSql::delete_view(&delete_id, conn) {
-                            Ok(view_table) => {
-                                let _ = notify_view_num_did_change(&view_table.belong_to_id, conn)?;
-                            },
-                            Err(e) => log::error!("Delete view: {} failed: {:?}", delete_id, e),
-                        }
+                    for view_id in delete_ids {
+                        let _ = ViewTableSql::delete_view(&view_id, conn)?;
+                        let _ = document.delete(view_id.clone().into())?;
+                        let _ = notify_view_num_did_change(&view_id, conn)?;
                     }
                     Ok(())
                 })?;
@@ -288,3 +254,12 @@ fn handle_trash_event(database: Arc<dyn WorkspaceDatabase>, event: TrashEvent) {
         },
     }
 }
+
+fn notify_view_num_did_change(view_id: &str, conn: &SqliteConnection) -> WorkspaceResult<()> {
+    let view_table = ViewTableSql::read_view(view_id, conn)?;
+    let repeated_view = ViewTableSql::read_views(&view_table.belong_to_id, conn)?;
+    send_dart_notification(&view_table.belong_to_id, WorkspaceNotification::AppViewsChanged)
+        .payload(repeated_view)
+        .send();
+    Ok(())
+}

+ 13 - 4
rust-lib/flowy-workspace/src/sql_tables/trash/trash_table.rs

@@ -24,6 +24,19 @@ impl std::convert::Into<Trash> for TrashTable {
     }
 }
 
+impl std::convert::From<Trash> for TrashTable {
+    fn from(trash: Trash) -> Self {
+        TrashTable {
+            id: trash.id,
+            name: trash.name,
+            desc: "".to_owned(),
+            modified_time: trash.modified_time,
+            create_time: trash.create_time,
+            ty: trash.ty.into(),
+        }
+    }
+}
+
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)]
 #[repr(i32)]
 #[sql_type = "Integer"]
@@ -42,10 +55,6 @@ impl std::convert::From<i32> for SqlTrashType {
     }
 }
 
-impl SqlTrashType {
-    pub(crate) fn value(&self) -> i32 { *self as i32 }
-}
-
 impl_sql_integer_expression!(SqlTrashType);
 
 impl std::convert::Into<TrashType> for SqlTrashType {

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

@@ -35,9 +35,7 @@ impl ViewTableSql {
             .filter(view_table::id.eq(view_id))
             .first::<ViewTable>(conn)?;
 
-        let repeated_trash: Vec<String> = trash_table::dsl::trash_table
-            .select(trash_table::dsl::id)
-            .load(conn)?;
+        let repeated_trash: Vec<String> = trash_table::dsl::trash_table.select(trash_table::dsl::id).load(conn)?;
 
         if repeated_trash.contains(&view_table.id) {
             return Err(WorkspaceError::not_found());
@@ -53,9 +51,7 @@ impl ViewTableSql {
             .into_boxed()
             .load::<ViewTable>(conn)?;
 
-        let repeated_trash: Vec<String> = trash_table::dsl::trash_table
-            .select(trash_table::dsl::id)
-            .load(conn)?;
+        let repeated_trash: Vec<String> = trash_table::dsl::trash_table.select(trash_table::dsl::id).load(conn)?;
 
         view_tables = view_tables
             .into_iter()
@@ -75,12 +71,9 @@ impl ViewTableSql {
         Ok(())
     }
 
-    pub(crate) fn delete_view(view_id: &str, conn: &SqliteConnection) -> Result<ViewTable, WorkspaceError> {
-        let view_table = dsl::view_table
-            .filter(view_table::id.eq(view_id))
-            .first::<ViewTable>(conn)?;
+    pub(crate) fn delete_view(view_id: &str, conn: &SqliteConnection) -> Result<(), WorkspaceError> {
         diesel_delete_table!(view_table, view_id, conn);
-        Ok(view_table)
+        Ok(())
     }
 }
 

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

@@ -1,5 +1,8 @@
 use crate::{
-    entities::view::{RepeatedView, UpdateViewParams, View, ViewType},
+    entities::{
+        trash::{Trash, TrashType},
+        view::{RepeatedView, UpdateViewParams, View, ViewType},
+    },
     sql_tables::app::AppTable,
 };
 use diesel::sql_types::Integer;
@@ -65,6 +68,18 @@ impl std::convert::Into<View> for ViewTable {
     }
 }
 
+impl std::convert::Into<Trash> for ViewTable {
+    fn into(self) -> Trash {
+        Trash {
+            id: self.id,
+            name: self.name,
+            modified_time: self.modified_time,
+            create_time: self.create_time,
+            ty: TrashType::View,
+        }
+    }
+}
+
 #[derive(AsChangeset, Identifiable, Clone, Default, Debug)]
 #[table_name = "view_table"]
 pub(crate) struct ViewTableChangeset {
@@ -73,7 +88,6 @@ pub(crate) struct ViewTableChangeset {
     pub desc: Option<String>,
     pub thumbnail: Option<String>,
     pub modified_time: i64,
-    pub is_trash: Option<bool>,
 }
 
 impl ViewTableChangeset {
@@ -84,7 +98,6 @@ impl ViewTableChangeset {
             desc: params.desc,
             thumbnail: params.thumbnail,
             modified_time: timestamp(),
-            is_trash: params.is_trash,
         }
     }
 
@@ -95,7 +108,6 @@ impl ViewTableChangeset {
             desc: Some(table.desc),
             thumbnail: Some(table.thumbnail),
             modified_time: table.modified_time,
-            is_trash: Some(table.is_trash),
         }
     }
 }

+ 10 - 10
rust-lib/flowy-workspace/tests/workspace/app_test.rs

@@ -50,17 +50,17 @@ async fn app_create_with_view() {
 // #[tokio::test]
 // async fn app_set_trash_flag() {
 //     let test = AppTest::new().await;
-//     test.move_app_to_trash().await;
+//     test.delete().await;
 //
 //     let query = QueryAppRequest::new(&test.app.id).trash();
 //     let _ = read_app(&test.sdk, query);
 // }
-
-#[tokio::test]
-#[should_panic]
-async fn app_set_trash_flag_2() {
-    let test = AppTest::new().await;
-    test.move_app_to_trash().await;
-    let query = QueryAppRequest::new(&test.app.id);
-    let _ = read_app(&test.sdk, query);
-}
+//
+// #[tokio::test]
+// #[should_panic]
+// async fn app_set_trash_flag_2() {
+//     let test = AppTest::new().await;
+//     test.move_app_to_trash().await;
+//     let query = QueryAppRequest::new(&test.app.id);
+//     let _ = read_app(&test.sdk, query);
+// }

+ 2 - 15
rust-lib/flowy-workspace/tests/workspace/view_test.rs

@@ -1,27 +1,14 @@
 use flowy_test::{workspace::*, FlowyTest};
 use flowy_workspace::entities::view::*;
 
-// #[tokio::test]
-// async fn view_move_to_trash() {
-//     let test = FlowyTest::setup();
-//     let _ = test.init_user().await;
-//
-//     let test = ViewTest::new(&test).await;
-//     test.move_view_to_trash().await;
-//
-//     let query = QueryViewRequest::new(&test.view.id).trash();
-//     let view = read_view(&test.sdk, query).await;
-//     assert_eq!(view, test.view);
-// }
-
 #[tokio::test]
 #[should_panic]
-async fn view_move_to_trash2() {
+async fn view_move_to_trash() {
     let test = FlowyTest::setup();
     let _ = test.init_user();
 
     let test = ViewTest::new(&test).await;
-    test.move_view_to_trash().await;
+    test.delete().await;
     let query = QueryViewRequest::new(&test.view.id);
     let _ = read_view(&test.sdk, query).await;
 }