Browse Source

[client]: fix trash notification issue

appflowy 3 years ago
parent
commit
2513853ea4

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

@@ -40,7 +40,7 @@ class ApplicationBlocObserver extends BlocObserver {
   @override
   // ignore: unnecessary_overrides
   void onTransition(Bloc bloc, Transition transition) {
-    // Log.debug("[current]: ${transition.currentState} \n[next]: ${transition.nextState}");
+    Log.debug("[current]: ${transition.currentState} \n[next]: ${transition.nextState}");
     super.onTransition(bloc, transition);
   }
 

+ 4 - 1
app_flowy/lib/workspace/application/trash/trash_bloc.dart

@@ -28,7 +28,10 @@ class TrashBloc extends Bloc<TrashEvent, TrashState> {
       },
       putback: (e) async* {
         final result = await iTrash.putback(e.trashId);
-        result.fold((l) {}, (error) {});
+        yield result.fold(
+          (l) => state.copyWith(successOrFailure: left(unit)),
+          (error) => state.copyWith(successOrFailure: right(error)),
+        );
       },
       delete: (e) async* {
         final result = await iTrash.delete(e.trashId);

+ 1 - 1
app_flowy/lib/workspace/application/view/view_bloc.dart

@@ -32,7 +32,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
         (error) => state.copyWith(successOrFailure: right(error)),
       );
     }, delete: (e) async* {
-      final result = await iViewImpl.pushIntoTrash();
+      final result = await iViewImpl.delete();
       yield result.fold(
         (l) => state.copyWith(successOrFailure: left(unit)),
         (error) => state.copyWith(successOrFailure: right(error)),

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

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

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

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

+ 1 - 1
app_flowy/lib/workspace/domain/i_view.dart

@@ -7,7 +7,7 @@ typedef ViewUpdatedCallback = void Function(Either<View, WorkspaceError>);
 abstract class IView {
   View get view;
 
-  Future<Either<Unit, WorkspaceError>> pushIntoTrash();
+  Future<Either<Unit, WorkspaceError>> delete();
 
   Future<Either<View, WorkspaceError>> rename(String newName);
 }

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

@@ -13,8 +13,8 @@ class IViewImpl extends IView {
   View get view => repo.view;
 
   @override
-  Future<Either<Unit, WorkspaceError>> pushIntoTrash() {
-    return repo.updateView(isTrash: true).then((result) {
+  Future<Either<Unit, WorkspaceError>> delete() {
+    return repo.delete().then((result) {
       return result.fold(
         (_) => left(unit),
         (error) => right(error),

+ 2 - 6
app_flowy/lib/workspace/infrastructure/repos/app_repo.dart

@@ -21,9 +21,7 @@ class AppRepository {
   });
 
   Future<Either<App, WorkspaceError>> getAppDesc() {
-    final request = QueryAppRequest.create()
-      ..appId = appId
-      ..readBelongings = false;
+    final request = QueryAppRequest.create()..appId = appId;
 
     return WorkspaceEventReadApp(request).send();
   }
@@ -39,9 +37,7 @@ class AppRepository {
   }
 
   Future<Either<List<View>, WorkspaceError>> getViews() {
-    final request = QueryAppRequest.create()
-      ..appId = appId
-      ..readBelongings = true;
+    final request = QueryAppRequest.create()..appId = appId;
 
     return WorkspaceEventReadApp(request).send().then((result) {
       return result.fold(

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

@@ -26,7 +26,7 @@ class ViewRepository {
     return WorkspaceEventReadView(request).send();
   }
 
-  Future<Either<View, WorkspaceError>> updateView({String? name, String? desc, bool? isTrash}) {
+  Future<Either<View, WorkspaceError>> updateView({String? name, String? desc}) {
     final request = UpdateViewRequest.create()..viewId = view.id;
 
     if (name != null) {
@@ -37,15 +37,11 @@ class ViewRepository {
       request.desc = desc;
     }
 
-    if (isTrash != null) {
-      request.isTrash = isTrash;
-    }
-
     return WorkspaceEventUpdateView(request).send();
   }
 
   Future<Either<Unit, WorkspaceError>> delete() {
-    final request = DeleteViewRequest.create()..viewId = view.id;
+    final request = DeleteViewRequest.create()..viewIds.add(view.id);
     return WorkspaceEventDeleteView(request).send();
   }
 }

+ 3 - 1
app_flowy/lib/workspace/presentation/stack_page/trash/trash_page.dart

@@ -149,7 +149,9 @@ class _TrashStackPageState extends State<TrashStackPage> {
                   height: 42,
                   child: TrashCell(
                     object: object,
-                    onRestore: () => context.read<TrashBloc>().add(TrashEvent.putback(object.id)),
+                    onRestore: () {
+                      context.read<TrashBloc>().add(TrashEvent.putback(object.id));
+                    },
                     onDelete: () => context.read<TrashBloc>().add(TrashEvent.delete(object.id)),
                   ),
                 );

+ 4 - 5
rust-lib/flowy-workspace/src/handlers/trash_handler.rs

@@ -1,16 +1,15 @@
 use crate::{
-    entities::{
-        trash::{RepeatedTrash, TrashIdentifier},
-    },
+    entities::trash::{RepeatedTrash, TrashIdentifier},
     errors::WorkspaceError,
     services::TrashCan,
 };
 use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit};
-use std::{sync::Arc};
+use std::sync::Arc;
 
 #[tracing::instrument(skip(controller), err)]
 pub(crate) async fn read_trash_handler(controller: Unit<Arc<TrashCan>>) -> DataResult<RepeatedTrash, WorkspaceError> {
-    let repeated_trash = controller.read_trash()?;
+    let conn = controller.database.db_connection()?;
+    let repeated_trash = controller.read_trash(&conn)?;
     data_result(repeated_trash)
 }
 

+ 27 - 11
rust-lib/flowy-workspace/src/services/trash_can.rs

@@ -41,7 +41,7 @@ impl TrashEvent {
 }
 
 pub struct TrashCan {
-    database: Arc<dyn WorkspaceDatabase>,
+    pub database: Arc<dyn WorkspaceDatabase>,
     notify: broadcast::Sender<TrashEvent>,
 }
 
@@ -51,16 +51,36 @@ impl TrashCan {
 
         Self { database, notify: tx }
     }
-    pub fn read_trash(&self) -> Result<RepeatedTrash, WorkspaceError> {
-        let conn = self.database.db_connection()?;
+
+    pub fn read_trash(&self, conn: &SqliteConnection) -> Result<RepeatedTrash, WorkspaceError> {
         let repeated_trash = TrashTableSql::read_all(&*conn)?;
         Ok(repeated_trash)
     }
 
+    pub fn trash_ids(&self, conn: &SqliteConnection) -> Result<Vec<String>, WorkspaceError> {
+        let ids = TrashTableSql::read_all(&*conn)?
+            .take_items()
+            .into_iter()
+            .map(|item| item.id)
+            .collect::<Vec<String>>();
+        Ok(ids)
+    }
+
     #[tracing::instrument(level = "debug", skip(self), fields(putback)  err)]
     pub async fn putback(&self, trash_id: &str) -> WorkspaceResult<()> {
         let (tx, mut rx) = mpsc::channel::<WorkspaceResult<()>>(1);
         let trash_table = TrashTableSql::read(trash_id, &*self.database.db_connection()?)?;
+        let _ = thread::scope(|_s| {
+            let conn = self.database.db_connection()?;
+            let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
+                let _ = TrashTableSql::delete_trash(trash_id, &*conn)?;
+                let _ = self.notify_dart_trash_did_update(&conn)?;
+                Ok(())
+            })?;
+            Ok::<(), WorkspaceError>(())
+        })
+        .unwrap()?;
+
         tracing::Span::current().record(
             "putback",
             &format!("{:?}: {}", &trash_table.ty, trash_table.id).as_str(),
@@ -70,12 +90,6 @@ impl TrashCan {
             .send(TrashEvent::Putback(trash_table.ty.into(), vec![trash_table.id], tx));
 
         let _ = rx.recv().await.unwrap()?;
-        let conn = self.database.db_connection()?;
-        let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
-            let _ = TrashTableSql::delete_trash(trash_id, &*conn)?;
-            let _ = self.notify_dart_trash_did_update(&conn)?;
-            Ok(())
-        })?;
         Ok(())
     }
 
@@ -122,10 +136,12 @@ impl TrashCan {
                     if trash_type.as_ref().unwrap() != &t.ty {
                         return Err(WorkspaceError::internal());
                     }
-
-                    ids.push(t.id.clone());
+                    let trash_id = t.id.clone();
+                    log::debug!("create trash: {:?}", t);
                     let _ = TrashTableSql::create_trash(t.into(), &*conn)?;
+                    ids.push(trash_id);
                 }
+                let _ = self.notify_dart_trash_did_update(&conn)?;
                 Ok(())
             })?;
             Ok::<(), WorkspaceError>(())

+ 47 - 27
rust-lib/flowy-workspace/src/services/view_controller.rs

@@ -59,12 +59,12 @@ impl ViewController {
     pub(crate) async fn create_view(&self, params: CreateViewParams) -> Result<View, WorkspaceError> {
         let view = self.create_view_on_server(params.clone()).await?;
         let conn = &*self.database.db_connection()?;
+        let trash_can = self.trash_can.clone();
         // TODO: rollback anything created before if failed?
         conn.immediate_transaction::<_, WorkspaceError, _>(|| {
             let _ = self.save_view(view.clone(), conn)?;
             self.document.create(CreateDocParams::new(&view.id, params.data))?;
-
-            let repeated_view = ViewTableSql::read_views(&view.belong_to_id, conn)?;
+            let repeated_view = read_belonging_view(&view.belong_to_id, trash_can, &conn)?;
             send_dart_notification(&view.belong_to_id, WorkspaceNotification::AppViewsChanged)
                 .payload(repeated_view)
                 .send();
@@ -112,7 +112,7 @@ impl ViewController {
     pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, WorkspaceError> {
         // TODO: read from server
         let conn = self.database.db_connection()?;
-        let repeated_view = ViewTableSql::read_views(belong_to_id, &*conn)?;
+        let repeated_view = read_belonging_view(belong_to_id, self.trash_can.clone(), &conn)?;
         Ok(repeated_view)
     }
 
@@ -165,22 +165,22 @@ impl ViewController {
         Ok(())
     }
 
-    #[tracing::instrument(skip(self), err)]
-    fn delete_view_on_server(&self, view_ids: Vec<String>) -> Result<(), WorkspaceError> {
-        let token = self.user.token()?;
-        let server = self.server.clone();
-        let params = DeleteViewParams { view_ids };
-        spawn(async move {
-            match server.delete_view(&token, params).await {
-                Ok(_) => {},
-                Err(e) => {
-                    // TODO: retry?
-                    log::error!("Delete view failed: {:?}", e);
-                },
-            }
-        });
-        Ok(())
-    }
+    // #[tracing::instrument(skip(self), err)]
+    // fn delete_view_on_server(&self, view_ids: Vec<String>) -> Result<(),
+    // WorkspaceError> {     let token = self.user.token()?;
+    //     let server = self.server.clone();
+    //     let params = DeleteViewParams { view_ids };
+    //     spawn(async move {
+    //         match server.delete_view(&token, params).await {
+    //             Ok(_) => {},
+    //             Err(e) => {
+    //                 // TODO: retry?
+    //                 log::error!("Delete view failed: {:?}", e);
+    //             },
+    //         }
+    //     });
+    //     Ok(())
+    // }
 
     #[tracing::instrument(skip(self), err)]
     fn read_view_on_server(&self, params: ViewIdentifier) -> Result<(), WorkspaceError> {
@@ -202,6 +202,7 @@ impl ViewController {
         let mut rx = self.trash_can.subscribe();
         let database = self.database.clone();
         let document = self.document.clone();
+        let trash_can = self.trash_can.clone();
         let _ = tokio::spawn(async move {
             loop {
                 let mut stream = Box::pin(rx.recv().into_stream().filter_map(|result| async move {
@@ -212,7 +213,9 @@ impl ViewController {
                 }));
                 let event: Option<TrashEvent> = stream.next().await;
                 match event {
-                    Some(event) => handle_trash_event(database.clone(), document.clone(), event),
+                    Some(event) => {
+                        handle_trash_event(database.clone(), document.clone(), trash_can.clone(), event).await
+                    },
                     None => {},
                 }
             }
@@ -220,7 +223,12 @@ impl ViewController {
     }
 }
 
-fn handle_trash_event(database: Arc<dyn WorkspaceDatabase>, document: Arc<FlowyDocument>, event: TrashEvent) {
+async fn handle_trash_event(
+    database: Arc<dyn WorkspaceDatabase>,
+    document: Arc<FlowyDocument>,
+    trash_can: Arc<TrashCan>,
+    event: TrashEvent,
+) {
     let db_result = database.db_connection();
 
     match event {
@@ -229,13 +237,13 @@ fn handle_trash_event(database: Arc<dyn WorkspaceDatabase>, document: Arc<FlowyD
                 let conn = &*db_result?;
                 let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
                     for view_id in view_ids {
-                        let _ = notify_view_num_did_change(&view_id, conn)?;
+                        let _ = notify_view_num_did_change(&view_id, conn, trash_can.clone())?;
                     }
                     Ok(())
                 })?;
                 Ok::<(), WorkspaceError>(())
             };
-            let _ = ret.send(result());
+            let _ = ret.send(result()).await;
         },
         TrashEvent::Delete(_, delete_ids, ret) => {
             let result = || {
@@ -244,22 +252,34 @@ fn handle_trash_event(database: Arc<dyn WorkspaceDatabase>, document: Arc<FlowyD
                     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)?;
+                        let _ = notify_view_num_did_change(&view_id, conn, trash_can.clone())?;
                     }
                     Ok(())
                 })?;
                 Ok::<(), WorkspaceError>(())
             };
-            let _ = ret.send(result());
+            let _ = ret.send(result()).await;
         },
     }
 }
 
-fn notify_view_num_did_change(view_id: &str, conn: &SqliteConnection) -> WorkspaceResult<()> {
+#[tracing::instrument(skip(conn, trash_can), err)]
+fn notify_view_num_did_change(view_id: &str, conn: &SqliteConnection, trash_can: Arc<TrashCan>) -> WorkspaceResult<()> {
     let view_table = ViewTableSql::read_view(view_id, conn)?;
-    let repeated_view = ViewTableSql::read_views(&view_table.belong_to_id, conn)?;
+    let repeated_view = read_belonging_view(&view_table.belong_to_id, trash_can, conn)?;
     send_dart_notification(&view_table.belong_to_id, WorkspaceNotification::AppViewsChanged)
         .payload(repeated_view)
         .send();
     Ok(())
 }
+
+fn read_belonging_view(
+    belong_to_id: &str,
+    trash_can: Arc<TrashCan>,
+    conn: &SqliteConnection,
+) -> WorkspaceResult<RepeatedView> {
+    let mut repeated_view = ViewTableSql::read_views(belong_to_id, conn)?;
+    let trash_ids = trash_can.trash_ids(conn)?;
+    repeated_view.retain(|view| !trash_ids.contains(&view.id));
+    Ok(repeated_view)
+}

+ 1 - 14
rust-lib/flowy-workspace/src/sql_tables/view/view_sql.rs

@@ -35,29 +35,16 @@ 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)?;
-
-        if repeated_trash.contains(&view_table.id) {
-            return Err(WorkspaceError::not_found());
-        }
-
         Ok(view_table)
     }
 
     // belong_to_id will be the app_id or view_id.
     pub(crate) fn read_views(belong_to_id: &str, conn: &SqliteConnection) -> Result<RepeatedView, WorkspaceError> {
-        let mut view_tables = dsl::view_table
+        let view_tables = dsl::view_table
             .filter(view_table::belong_to_id.eq(belong_to_id))
             .into_boxed()
             .load::<ViewTable>(conn)?;
 
-        let repeated_trash: Vec<String> = trash_table::dsl::trash_table.select(trash_table::dsl::id).load(conn)?;
-
-        view_tables = view_tables
-            .into_iter()
-            .filter(|table| !repeated_trash.contains(&table.id))
-            .collect::<Vec<ViewTable>>();
-
         let views = view_tables
             .into_iter()
             .map(|view_table| view_table.into())