Browse Source

add trash sql table

appflowy 3 years ago
parent
commit
3e65c66baf
26 changed files with 603 additions and 41 deletions
  1. 38 0
      app_flowy/lib/workspace/application/trash/trash_bloc.dart
  2. 304 0
      app_flowy/lib/workspace/application/trash/trash_bloc.freezed.dart
  3. 20 0
      app_flowy/lib/workspace/domain/i_trash.dart
  4. 0 0
      app_flowy/lib/workspace/infrastructure/repos/trash_repo.dart
  5. 14 0
      app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart
  6. 2 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart
  7. 2 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart
  8. 8 0
      rust-lib/flowy-database/migrations/2021-07-09-063045_flowy-user/up.sql
  9. 19 1
      rust-lib/flowy-database/src/schema.rs
  10. 1 0
      rust-lib/flowy-workspace/src/entities/mod.rs
  11. 5 0
      rust-lib/flowy-workspace/src/entities/trash/mod.rs
  12. 39 0
      rust-lib/flowy-workspace/src/entities/trash/trash_create.rs
  13. 7 0
      rust-lib/flowy-workspace/src/entities/trash/trash_delete.rs
  14. 3 0
      rust-lib/flowy-workspace/src/event.rs
  15. 2 0
      rust-lib/flowy-workspace/src/handlers/mod.rs
  16. 15 0
      rust-lib/flowy-workspace/src/handlers/trash_handler.rs
  17. 7 0
      rust-lib/flowy-workspace/src/module.rs
  18. 44 38
      rust-lib/flowy-workspace/src/protobuf/model/event.rs
  19. 1 0
      rust-lib/flowy-workspace/src/protobuf/proto/event.proto
  20. 2 0
      rust-lib/flowy-workspace/src/services/mod.rs
  21. 8 0
      rust-lib/flowy-workspace/src/services/trash_controller.rs
  22. 4 1
      rust-lib/flowy-workspace/src/services/workspace_controller.rs
  23. 1 0
      rust-lib/flowy-workspace/src/sql_tables/mod.rs
  24. 5 0
      rust-lib/flowy-workspace/src/sql_tables/trash/mod.rs
  25. 28 0
      rust-lib/flowy-workspace/src/sql_tables/trash/trash_sql.rs
  26. 24 0
      rust-lib/flowy-workspace/src/sql_tables/trash/trash_table.rs

+ 38 - 0
app_flowy/lib/workspace/application/trash/trash_bloc.dart

@@ -0,0 +1,38 @@
+import 'package:app_flowy/workspace/domain/i_trash.dart';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+part 'trash_bloc.freezed.dart';
+
+class TrashBloc extends Bloc<TrashEvent, TrashState> {
+  final ITrash iTrash;
+  TrashBloc(this.iTrash) : super(TrashState.init());
+
+  @override
+  Stream<TrashState> mapEventToState(TrashEvent event) async* {
+    yield* event.map(
+      initial: (e) async* {
+        yield state;
+      },
+    );
+  }
+}
+
+@freezed
+class TrashEvent with _$TrashEvent {
+  const factory TrashEvent.initial() = Initial;
+}
+
+@freezed
+class TrashState with _$TrashState {
+  const factory TrashState({
+    required List<TrashObject> objects,
+    required Either<Unit, WorkspaceError> successOrFailure,
+  }) = _TrashState;
+
+  factory TrashState.init() => TrashState(
+        objects: [],
+        successOrFailure: left(unit),
+      );
+}

+ 304 - 0
app_flowy/lib/workspace/application/trash/trash_bloc.freezed.dart

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

+ 20 - 0
app_flowy/lib/workspace/domain/i_trash.dart

@@ -0,0 +1,20 @@
+import 'dart:async';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
+
+abstract class TrashObject {
+  String get id;
+}
+
+abstract class ITrash {
+  Future<Either<List<TrashObject>, WorkspaceError>> readTrash();
+}
+
+typedef TrashUpdateCallback = void Function(List<TrashObject>);
+
+abstract class ITrashListener {
+  void start();
+  void setTrashUpdateCallback(TrashUpdateCallback trashUpdateCallback);
+  Future<void> stop();
+}

+ 0 - 0
app_flowy/lib/workspace/infrastructure/repos/trash_repo.dart


+ 14 - 0
app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart

@@ -271,6 +271,20 @@ class WorkspaceEventApplyDocDelta {
     }
 }
 
+class WorkspaceEventReadTrash {
+    WorkspaceEventReadTrash();
+
+    Future<Either<RepeatedView, WorkspaceError>> send() {
+     final request = FFIRequest.create()
+        ..event = WorkspaceEvent.ReadTrash.toString();
+
+     return Dispatch.asyncRequest(request).then((bytesResult) => bytesResult.fold(
+        (okBytes) => left(RepeatedView.fromBuffer(okBytes)),
+        (errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
+      ));
+    }
+}
+
 class WorkspaceEventInitWorkspace {
     WorkspaceEventInitWorkspace();
 

+ 2 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart

@@ -26,6 +26,7 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
   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 ReadTrash = WorkspaceEvent._(300, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadTrash');
   static const WorkspaceEvent InitWorkspace = WorkspaceEvent._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InitWorkspace');
 
   static const $core.List<WorkspaceEvent> values = <WorkspaceEvent> [
@@ -45,6 +46,7 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
     DeleteView,
     OpenView,
     ApplyDocDelta,
+    ReadTrash,
     InitWorkspace,
   ];
 

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

@@ -28,9 +28,10 @@ const WorkspaceEvent$json = const {
     const {'1': 'DeleteView', '2': 204},
     const {'1': 'OpenView', '2': 205},
     const {'1': 'ApplyDocDelta', '2': 206},
+    const {'1': 'ReadTrash', '2': 300},
     const {'1': 'InitWorkspace', '2': 1000},
   ],
 };
 
 /// Descriptor for `WorkspaceEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESDQoIT3BlblZpZXcQzQESEgoNQXBwbHlEb2NEZWx0YRDOARISCg1Jbml0V29ya3NwYWNlEOgH');
+final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESDQoIT3BlblZpZXcQzQESEgoNQXBwbHlEb2NEZWx0YRDOARIOCglSZWFkVHJhc2gQrAISEgoNSW5pdFdvcmtzcGFjZRDoBw==');

+ 8 - 0
rust-lib/flowy-database/migrations/2021-07-09-063045_flowy-user/up.sql

@@ -42,3 +42,11 @@ CREATE TABLE view_table (
     version BIGINT NOT NULL DEFAULT 0,
     is_trash Boolean NOT NULL DEFAULT false
 );
+
+CREATE TABLE trash_table (
+    id TEXT NOT NULL PRIMARY KEY,
+    name TEXT NOT NULL DEFAULT '',
+    desc TEXT NOT NULL DEFAULT '',
+    modified_time BIGINT NOT NULL DEFAULT 0,
+    create_time BIGINT NOT NULL DEFAULT 0
+);

+ 19 - 1
rust-lib/flowy-database/src/schema.rs

@@ -33,6 +33,16 @@ table! {
     }
 }
 
+table! {
+    trash_table (id) {
+        id -> Text,
+        name -> Text,
+        desc -> Text,
+        modified_time -> BigInt,
+        create_time -> BigInt,
+    }
+}
+
 table! {
     user_table (id) {
         id -> Text,
@@ -70,4 +80,12 @@ table! {
     }
 }
 
-allow_tables_to_appear_in_same_query!(app_table, doc_table, rev_table, user_table, view_table, workspace_table,);
+allow_tables_to_appear_in_same_query!(
+    app_table,
+    doc_table,
+    rev_table,
+    trash_table,
+    user_table,
+    view_table,
+    workspace_table,
+);

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

@@ -1,3 +1,4 @@
 pub mod app;
+pub mod trash;
 pub mod view;
 pub mod workspace;

+ 5 - 0
rust-lib/flowy-workspace/src/entities/trash/mod.rs

@@ -0,0 +1,5 @@
+mod trash_create;
+mod trash_delete;
+
+pub use trash_create::*;
+pub use trash_delete::*;

+ 39 - 0
rust-lib/flowy-workspace/src/entities/trash/trash_create.rs

@@ -0,0 +1,39 @@
+use crate::errors::WorkspaceError;
+use flowy_derive::ProtoBuf;
+use std::convert::TryInto;
+
+#[derive(PartialEq, ProtoBuf, Default, Debug, Clone)]
+pub struct CreateTrashParams {
+    #[pb(index = 1)]
+    pub id: String,
+
+    #[pb(index = 2)]
+    pub name: String,
+
+    #[pb(index = 3)]
+    pub modified_time: i64,
+
+    #[pb(index = 4)]
+    pub create_time: i64,
+}
+
+#[derive(PartialEq, ProtoBuf, Default, Debug, Clone)]
+pub struct Trash {
+    #[pb(index = 1)]
+    pub id: String,
+
+    #[pb(index = 2)]
+    pub name: String,
+
+    #[pb(index = 3)]
+    pub modified_time: i64,
+
+    #[pb(index = 4)]
+    pub create_time: i64,
+}
+
+#[derive(PartialEq, Debug, Default, ProtoBuf, Clone)]
+pub struct RepeatedTrash {
+    #[pb(index = 1)]
+    pub items: Vec<Trash>,
+}

+ 7 - 0
rust-lib/flowy-workspace/src/entities/trash/trash_delete.rs

@@ -0,0 +1,7 @@
+use flowy_derive::ProtoBuf;
+
+#[derive(PartialEq, ProtoBuf, Default, Debug, Clone)]
+pub struct DeleteTrashRequest {
+    #[pb(index = 1)]
+    pub id: String,
+}

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

@@ -52,6 +52,9 @@ pub enum WorkspaceEvent {
     #[event(input = "DocDelta", output = "DocDelta")]
     ApplyDocDelta     = 206,
 
+    #[event(output = "DocDelta", output = "RepeatedView")]
+    ReadTrash         = 300,
+
     #[event()]
     InitWorkspace     = 1000,
 }

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

@@ -1,7 +1,9 @@
 mod app_handler;
+mod trash_handler;
 mod view_handler;
 mod workspace_handler;
 
 pub(crate) use app_handler::*;
+pub(crate) use trash_handler::*;
 pub(crate) use view_handler::*;
 pub(crate) use workspace_handler::*;

+ 15 - 0
rust-lib/flowy-workspace/src/handlers/trash_handler.rs

@@ -0,0 +1,15 @@
+use crate::{
+    entities::{trash::RepeatedTrash, view::RepeatedView},
+    errors::WorkspaceError,
+    services::TrashController,
+};
+use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit};
+use std::{convert::TryInto, sync::Arc};
+
+#[tracing::instrument(skip(data, controller), err)]
+pub(crate) async fn read_trash_handler(
+    controller: Unit<Arc<TrashController>>,
+) -> DataResult<RepeatedTrash, WorkspaceError> {
+    let repeated_trash = controller.read_trash()?;
+    data_result(repeated_trash)
+}

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

@@ -5,6 +5,7 @@ use crate::{
     services::{server::construct_workspace_server, AppController, ViewController, WorkspaceController},
 };
 
+use crate::services::TrashController;
 use flowy_database::DBConnection;
 use flowy_dispatch::prelude::*;
 use flowy_document::module::FlowyDocument;
@@ -37,6 +38,8 @@ pub fn mk_workspace(
 ) -> Arc<WorkspaceController> {
     let server = construct_workspace_server(server_config);
 
+    let trash_controller = Arc::new(TrashController::new());
+
     let view_controller = Arc::new(ViewController::new(
         user.clone(),
         database.clone(),
@@ -51,6 +54,7 @@ pub fn mk_workspace(
         database.clone(),
         app_controller.clone(),
         view_controller.clone(),
+        trash_controller.clone(),
         server.clone(),
     ));
     workspace_controller
@@ -62,6 +66,7 @@ pub fn create(workspace: Arc<WorkspaceController>) -> Module {
         .data(workspace.clone())
         .data(workspace.app_controller.clone())
         .data(workspace.view_controller.clone())
+        .data(workspace.trash_controller.clone())
         .event(WorkspaceEvent::InitWorkspace, init_workspace_handler);
 
     module = module
@@ -85,5 +90,7 @@ pub fn create(workspace: Arc<WorkspaceController>) -> Module {
         .event(WorkspaceEvent::OpenView, open_view_handler)
         .event(WorkspaceEvent::ApplyDocDelta, apply_doc_delta_handler);
 
+    module = module.event(WorkspaceEvent::ReadTrash, read_trash_handler);
+
     module
 }

+ 44 - 38
rust-lib/flowy-workspace/src/protobuf/model/event.rs

@@ -41,6 +41,7 @@ pub enum WorkspaceEvent {
     DeleteView = 204,
     OpenView = 205,
     ApplyDocDelta = 206,
+    ReadTrash = 300,
     InitWorkspace = 1000,
 }
 
@@ -67,6 +68,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
             204 => ::std::option::Option::Some(WorkspaceEvent::DeleteView),
             205 => ::std::option::Option::Some(WorkspaceEvent::OpenView),
             206 => ::std::option::Option::Some(WorkspaceEvent::ApplyDocDelta),
+            300 => ::std::option::Option::Some(WorkspaceEvent::ReadTrash),
             1000 => ::std::option::Option::Some(WorkspaceEvent::InitWorkspace),
             _ => ::std::option::Option::None
         }
@@ -90,6 +92,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
             WorkspaceEvent::DeleteView,
             WorkspaceEvent::OpenView,
             WorkspaceEvent::ApplyDocDelta,
+            WorkspaceEvent::ReadTrash,
             WorkspaceEvent::InitWorkspace,
         ];
         values
@@ -119,7 +122,7 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0bevent.proto*\xc1\x02\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\
+    \n\x0bevent.proto*\xd1\x02\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\
@@ -127,43 +130,46 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \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\
-    \x12\n\rInitWorkspace\x10\xe8\x07J\xe3\x05\n\x06\x12\x04\0\0\x14\x01\n\
-    \x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x14\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\x19\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\x18b\x06proto3\
+    \x0e\n\tReadTrash\x10\xac\x02\x12\x12\n\rInitWorkspace\x10\xe8\x07J\x8c\
+    \x06\n\x06\x12\x04\0\0\x15\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
+    \x05\0\x12\x04\x02\0\x15\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\x19\n\x0c\
+    \n\x05\x05\0\x02\x11\x01\x12\x03\x14\x04\x11\n\x0c\n\x05\x05\0\x02\x11\
+    \x02\x12\x03\x14\x14\x18b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -17,5 +17,6 @@ enum WorkspaceEvent {
     DeleteView = 204;
     OpenView = 205;
     ApplyDocDelta = 206;
+    ReadTrash = 300;
     InitWorkspace = 1000;
 }

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

@@ -1,4 +1,5 @@
 pub(crate) use app_controller::*;
+pub(crate) use trash_controller::*;
 pub(crate) use view_controller::*;
 pub use workspace_controller::*;
 
@@ -6,5 +7,6 @@ mod app_controller;
 mod database;
 mod helper;
 pub mod server;
+mod trash_controller;
 mod view_controller;
 mod workspace_controller;

+ 8 - 0
rust-lib/flowy-workspace/src/services/trash_controller.rs

@@ -0,0 +1,8 @@
+use crate::{entities::trash::RepeatedTrash, errors::WorkspaceError};
+
+pub struct TrashController {}
+
+impl TrashController {
+    pub fn new() -> Self { Self {} }
+    pub fn read_trash(&self) -> Result<RepeatedTrash, WorkspaceError> { Ok(RepeatedTrash { items: vec![] }) }
+}

+ 4 - 1
rust-lib/flowy-workspace/src/services/workspace_controller.rs

@@ -6,7 +6,7 @@ use crate::{
     errors::*,
     module::{WorkspaceDatabase, WorkspaceUser},
     notify::*,
-    services::{helper::spawn, server::Server, AppController, ViewController},
+    services::{helper::spawn, server::Server, AppController, TrashController, ViewController},
     sql_tables::workspace::{WorkspaceTable, WorkspaceTableChangeset, WorkspaceTableSql},
 };
 use flowy_database::SqliteConnection;
@@ -19,6 +19,7 @@ pub struct WorkspaceController {
     pub(crate) view_controller: Arc<ViewController>,
     pub(crate) database: Arc<dyn WorkspaceDatabase>,
     pub(crate) app_controller: Arc<AppController>,
+    pub(crate) trash_controller: Arc<TrashController>,
     server: Server,
 }
 
@@ -28,6 +29,7 @@ impl WorkspaceController {
         database: Arc<dyn WorkspaceDatabase>,
         app_controller: Arc<AppController>,
         view_controller: Arc<ViewController>,
+        trash_controller: Arc<TrashController>,
         server: Server,
     ) -> Self {
         let workspace_sql = Arc::new(WorkspaceTableSql {});
@@ -37,6 +39,7 @@ impl WorkspaceController {
             database,
             app_controller,
             view_controller,
+            trash_controller,
             server,
         }
     }

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

@@ -1,3 +1,4 @@
 pub mod app;
+pub mod trash;
 pub mod view;
 pub mod workspace;

+ 5 - 0
rust-lib/flowy-workspace/src/sql_tables/trash/mod.rs

@@ -0,0 +1,5 @@
+mod trash_sql;
+mod trash_table;
+
+pub(crate) use trash_sql::*;
+pub(crate) use trash_table::*;

+ 28 - 0
rust-lib/flowy-workspace/src/sql_tables/trash/trash_sql.rs

@@ -0,0 +1,28 @@
+use crate::{entities::trash::RepeatedTrash, errors::WorkspaceError, sql_tables::trash::TrashTable};
+
+use crate::entities::trash::Trash;
+use flowy_database::{
+    prelude::*,
+    schema::{trash_table, trash_table::dsl},
+    SqliteConnection,
+};
+
+pub struct TrashTableSql {}
+
+impl TrashTableSql {
+    pub(crate) fn create_trash(&self, trash_table: TrashTable, conn: &SqliteConnection) -> Result<(), WorkspaceError> {
+        diesel_insert_table!(trash_table, &trash_table, conn);
+        Ok(())
+    }
+
+    pub(crate) fn read_trash(&self, conn: &SqliteConnection) -> Result<RepeatedTrash, WorkspaceError> {
+        let trash_tables = dsl::trash_table.load::<TrashTable>(conn)?;
+        let items = trash_tables.into_iter().map(|t| t.into()).collect::<Vec<Trash>>();
+        Ok(RepeatedTrash { items })
+    }
+
+    pub(crate) fn delete_trash(&self, trash_id: &str, conn: &SqliteConnection) -> Result<(), WorkspaceError> {
+        diesel_delete_table!(trash_table, trash_id, conn);
+        Ok(())
+    }
+}

+ 24 - 0
rust-lib/flowy-workspace/src/sql_tables/trash/trash_table.rs

@@ -0,0 +1,24 @@
+use crate::entities::trash::Trash;
+use diesel::sql_types::Binary;
+use flowy_database::schema::trash_table;
+
+#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
+#[table_name = "trash_table"]
+pub(crate) struct TrashTable {
+    pub id: String,
+    pub name: String,
+    pub desc: String,
+    pub modified_time: i64,
+    pub create_time: i64,
+}
+
+impl std::convert::Into<Trash> for TrashTable {
+    fn into(self) -> Trash {
+        Trash {
+            id: self.id,
+            name: self.name,
+            modified_time: self.modified_time,
+            create_time: self.create_time,
+        }
+    }
+}