瀏覽代碼

[flutter]: config trash list

appflowy 3 年之前
父節點
當前提交
931c638b93
共有 50 個文件被更改,包括 797 次插入400 次删除
  1. 1 1
      app_flowy/lib/startup/tasks/sdk_task.dart
  2. 6 0
      app_flowy/lib/workspace/application/app/app_listen_bloc.dart
  3. 32 3
      app_flowy/lib/workspace/application/trash/trash_bloc.dart
  4. 138 11
      app_flowy/lib/workspace/application/trash/trash_bloc.freezed.dart
  5. 4 8
      app_flowy/lib/workspace/domain/i_trash.dart
  6. 24 16
      app_flowy/lib/workspace/infrastructure/deps_resolver.dart
  7. 1 1
      app_flowy/lib/workspace/infrastructure/i_app_impl.dart
  8. 38 0
      app_flowy/lib/workspace/infrastructure/i_trash_impl.dart
  9. 9 9
      app_flowy/lib/workspace/infrastructure/i_user_impl.dart
  10. 1 1
      app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart
  11. 8 8
      app_flowy/lib/workspace/infrastructure/repos/app_repo.dart
  12. 3 4
      app_flowy/lib/workspace/infrastructure/repos/doc_repo.dart
  13. 15 13
      app_flowy/lib/workspace/infrastructure/repos/helper.dart
  14. 40 0
      app_flowy/lib/workspace/infrastructure/repos/trash_repo.dart
  15. 6 6
      app_flowy/lib/workspace/infrastructure/repos/view_repo.dart
  16. 9 9
      app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart
  17. 37 32
      app_flowy/lib/workspace/presentation/stack_page/trash/trash_page.dart
  18. 0 0
      app_flowy/lib/workspace/presentation/stack_page/trash/widget/list_body.dart
  19. 0 1
      app_flowy/lib/workspace/presentation/stack_page/trash/widget/list_header.dart
  20. 1 1
      app_flowy/lib/workspace/presentation/stack_page/trash/widget/sizes.dart
  21. 38 0
      app_flowy/lib/workspace/presentation/stack_page/trash/widget/trash_cell.dart
  22. 71 0
      app_flowy/lib/workspace/presentation/stack_page/trash/widget/trash_header.dart
  23. 1 1
      app_flowy/lib/workspace/presentation/widgets/menu/widget/menu_trash.dart
  24. 8 5
      app_flowy/packages/flowy_infra_ui/lib/style_widget/text.dart
  25. 23 23
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-dart-notify/subject.pb.dart
  26. 5 5
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-dart-notify/subject.pbjson.dart
  27. 9 9
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/observable.pbenum.dart
  28. 5 5
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/observable.pbjson.dart
  29. 18 16
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbenum.dart
  30. 6 5
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbjson.dart
  31. 6 7
      app_flowy/packages/flowy_sdk/lib/rust_stream.dart
  32. 3 3
      rust-lib/flowy-dart-notify/src/dart/stream_sender.rs
  33. 3 3
      rust-lib/flowy-dart-notify/src/entities/subject.rs
  34. 2 2
      rust-lib/flowy-dart-notify/src/lib.rs
  35. 57 57
      rust-lib/flowy-dart-notify/src/protobuf/model/subject.rs
  36. 1 1
      rust-lib/flowy-dart-notify/src/protobuf/proto/subject.proto
  37. 3 3
      rust-lib/flowy-derive/src/derive_cache/derive_cache.rs
  38. 5 5
      rust-lib/flowy-user/src/notify/observable.rs
  39. 20 20
      rust-lib/flowy-user/src/protobuf/model/observable.rs
  40. 1 1
      rust-lib/flowy-user/src/protobuf/proto/observable.proto
  41. 3 1
      rust-lib/flowy-user/src/services/server/server_api.rs
  42. 9 9
      rust-lib/flowy-user/src/services/user/user_session.rs
  43. 12 7
      rust-lib/flowy-workspace/src/notify/observable.rs
  44. 68 62
      rust-lib/flowy-workspace/src/protobuf/model/observable.rs
  45. 2 1
      rust-lib/flowy-workspace/src/protobuf/proto/observable.proto
  46. 5 3
      rust-lib/flowy-workspace/src/services/app_controller.rs
  47. 3 1
      rust-lib/flowy-workspace/src/services/server/middleware.rs
  48. 27 11
      rust-lib/flowy-workspace/src/services/trash_can.rs
  49. 6 6
      rust-lib/flowy-workspace/src/services/view_controller.rs
  50. 4 4
      rust-lib/flowy-workspace/src/services/workspace_controller.rs

+ 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);
   }
 

+ 6 - 0
app_flowy/lib/workspace/application/app/app_listen_bloc.dart

@@ -33,6 +33,12 @@ class AppListenBloc extends Bloc<AppListenEvent, AppListenState> {
       (error) => add(AppListenEvent.didReceiveViews(right(error))),
     );
   }
+
+  @override
+  Future<void> close() async {
+    await listener.stop();
+    return super.close();
+  }
 }
 
 @freezed

+ 32 - 3
app_flowy/lib/workspace/application/trash/trash_bloc.dart

@@ -1,33 +1,62 @@
 import 'package:app_flowy/workspace/domain/i_trash.dart';
 import 'package:dartz/dartz.dart';
+import 'package:flowy_log/flowy_log.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/trash_create.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());
+  final ITrashListener listener;
+  TrashBloc({required this.iTrash, required this.listener}) : super(TrashState.init());
 
   @override
   Stream<TrashState> mapEventToState(TrashEvent event) async* {
     yield* event.map(
       initial: (e) async* {
-        yield state;
+        listener.start(_listenTrashUpdated);
+        final result = await iTrash.readTrash();
+        yield result.fold(
+          (objects) => state.copyWith(objects: objects, successOrFailure: left(unit)),
+          (error) => state.copyWith(successOrFailure: right(error)),
+        );
+      },
+      didReceiveTrash: (e) async* {
+        yield state.copyWith(objects: e.trash);
       },
     );
   }
+
+  void _listenTrashUpdated(Either<List<Trash>, WorkspaceError> trashOrFailed) {
+    trashOrFailed.fold(
+      (trash) {
+        add(TrashEvent.didReceiveTrash(trash));
+      },
+      (error) {
+        Log.error(error);
+      },
+    );
+  }
+
+  @override
+  Future<void> close() async {
+    await listener.stop();
+    return super.close();
+  }
 }
 
 @freezed
 class TrashEvent with _$TrashEvent {
   const factory TrashEvent.initial() = Initial;
+  const factory TrashEvent.didReceiveTrash(List<Trash> trash) = ReceiveTrash;
 }
 
 @freezed
 class TrashState with _$TrashState {
   const factory TrashState({
-    required List<TrashObject> objects,
+    required List<Trash> objects,
     required Either<Unit, WorkspaceError> successOrFailure,
   }) = _TrashState;
 

+ 138 - 11
app_flowy/lib/workspace/application/trash/trash_bloc.freezed.dart

@@ -19,6 +19,12 @@ class _$TrashEventTearOff {
   Initial initial() {
     return const Initial();
   }
+
+  ReceiveTrash didReceiveTrash(List<Trash> trash) {
+    return ReceiveTrash(
+      trash,
+    );
+  }
 }
 
 /// @nodoc
@@ -29,22 +35,26 @@ mixin _$TrashEvent {
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
+    required TResult Function(List<Trash> trash) didReceiveTrash,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
+    TResult Function(List<Trash> trash)? didReceiveTrash,
     required TResult orElse(),
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
     required TResult Function(Initial value) initial,
+    required TResult Function(ReceiveTrash value) didReceiveTrash,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(Initial value)? initial,
+    TResult Function(ReceiveTrash value)? didReceiveTrash,
     required TResult orElse(),
   }) =>
       throw _privateConstructorUsedError;
@@ -104,6 +114,7 @@ class _$Initial implements Initial {
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
+    required TResult Function(List<Trash> trash) didReceiveTrash,
   }) {
     return initial();
   }
@@ -112,6 +123,7 @@ class _$Initial implements Initial {
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
+    TResult Function(List<Trash> trash)? didReceiveTrash,
     required TResult orElse(),
   }) {
     if (initial != null) {
@@ -124,6 +136,7 @@ class _$Initial implements Initial {
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
     required TResult Function(Initial value) initial,
+    required TResult Function(ReceiveTrash value) didReceiveTrash,
   }) {
     return initial(this);
   }
@@ -132,6 +145,7 @@ class _$Initial implements Initial {
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(Initial value)? initial,
+    TResult Function(ReceiveTrash value)? didReceiveTrash,
     required TResult orElse(),
   }) {
     if (initial != null) {
@@ -145,12 +159,127 @@ abstract class Initial implements TrashEvent {
   const factory Initial() = _$Initial;
 }
 
+/// @nodoc
+abstract class $ReceiveTrashCopyWith<$Res> {
+  factory $ReceiveTrashCopyWith(
+          ReceiveTrash value, $Res Function(ReceiveTrash) then) =
+      _$ReceiveTrashCopyWithImpl<$Res>;
+  $Res call({List<Trash> trash});
+}
+
+/// @nodoc
+class _$ReceiveTrashCopyWithImpl<$Res> extends _$TrashEventCopyWithImpl<$Res>
+    implements $ReceiveTrashCopyWith<$Res> {
+  _$ReceiveTrashCopyWithImpl(
+      ReceiveTrash _value, $Res Function(ReceiveTrash) _then)
+      : super(_value, (v) => _then(v as ReceiveTrash));
+
+  @override
+  ReceiveTrash get _value => super._value as ReceiveTrash;
+
+  @override
+  $Res call({
+    Object? trash = freezed,
+  }) {
+    return _then(ReceiveTrash(
+      trash == freezed
+          ? _value.trash
+          : trash // ignore: cast_nullable_to_non_nullable
+              as List<Trash>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$ReceiveTrash implements ReceiveTrash {
+  const _$ReceiveTrash(this.trash);
+
+  @override
+  final List<Trash> trash;
+
+  @override
+  String toString() {
+    return 'TrashEvent.didReceiveTrash(trash: $trash)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is ReceiveTrash &&
+            (identical(other.trash, trash) ||
+                const DeepCollectionEquality().equals(other.trash, trash)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(trash);
+
+  @JsonKey(ignore: true)
+  @override
+  $ReceiveTrashCopyWith<ReceiveTrash> get copyWith =>
+      _$ReceiveTrashCopyWithImpl<ReceiveTrash>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function(List<Trash> trash) didReceiveTrash,
+  }) {
+    return didReceiveTrash(trash);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function(List<Trash> trash)? didReceiveTrash,
+    required TResult orElse(),
+  }) {
+    if (didReceiveTrash != null) {
+      return didReceiveTrash(trash);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+    required TResult Function(ReceiveTrash value) didReceiveTrash,
+  }) {
+    return didReceiveTrash(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(ReceiveTrash value)? didReceiveTrash,
+    required TResult orElse(),
+  }) {
+    if (didReceiveTrash != null) {
+      return didReceiveTrash(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class ReceiveTrash implements TrashEvent {
+  const factory ReceiveTrash(List<Trash> trash) = _$ReceiveTrash;
+
+  List<Trash> get trash => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $ReceiveTrashCopyWith<ReceiveTrash> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
 /// @nodoc
 class _$TrashStateTearOff {
   const _$TrashStateTearOff();
 
   _TrashState call(
-      {required List<TrashObject> objects,
+      {required List<Trash> objects,
       required Either<Unit, WorkspaceError> successOrFailure}) {
     return _TrashState(
       objects: objects,
@@ -164,7 +293,7 @@ const $TrashState = _$TrashStateTearOff();
 
 /// @nodoc
 mixin _$TrashState {
-  List<TrashObject> get objects => throw _privateConstructorUsedError;
+  List<Trash> get objects => throw _privateConstructorUsedError;
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
 
@@ -179,8 +308,7 @@ abstract class $TrashStateCopyWith<$Res> {
           TrashState value, $Res Function(TrashState) then) =
       _$TrashStateCopyWithImpl<$Res>;
   $Res call(
-      {List<TrashObject> objects,
-      Either<Unit, WorkspaceError> successOrFailure});
+      {List<Trash> objects, Either<Unit, WorkspaceError> successOrFailure});
 }
 
 /// @nodoc
@@ -200,7 +328,7 @@ class _$TrashStateCopyWithImpl<$Res> implements $TrashStateCopyWith<$Res> {
       objects: objects == freezed
           ? _value.objects
           : objects // ignore: cast_nullable_to_non_nullable
-              as List<TrashObject>,
+              as List<Trash>,
       successOrFailure: successOrFailure == freezed
           ? _value.successOrFailure
           : successOrFailure // ignore: cast_nullable_to_non_nullable
@@ -216,8 +344,7 @@ abstract class _$TrashStateCopyWith<$Res> implements $TrashStateCopyWith<$Res> {
       __$TrashStateCopyWithImpl<$Res>;
   @override
   $Res call(
-      {List<TrashObject> objects,
-      Either<Unit, WorkspaceError> successOrFailure});
+      {List<Trash> objects, Either<Unit, WorkspaceError> successOrFailure});
 }
 
 /// @nodoc
@@ -239,7 +366,7 @@ class __$TrashStateCopyWithImpl<$Res> extends _$TrashStateCopyWithImpl<$Res>
       objects: objects == freezed
           ? _value.objects
           : objects // ignore: cast_nullable_to_non_nullable
-              as List<TrashObject>,
+              as List<Trash>,
       successOrFailure: successOrFailure == freezed
           ? _value.successOrFailure
           : successOrFailure // ignore: cast_nullable_to_non_nullable
@@ -254,7 +381,7 @@ class _$_TrashState implements _TrashState {
   const _$_TrashState({required this.objects, required this.successOrFailure});
 
   @override
-  final List<TrashObject> objects;
+  final List<Trash> objects;
   @override
   final Either<Unit, WorkspaceError> successOrFailure;
 
@@ -289,11 +416,11 @@ class _$_TrashState implements _TrashState {
 
 abstract class _TrashState implements TrashState {
   const factory _TrashState(
-      {required List<TrashObject> objects,
+      {required List<Trash> objects,
       required Either<Unit, WorkspaceError> successOrFailure}) = _$_TrashState;
 
   @override
-  List<TrashObject> get objects => throw _privateConstructorUsedError;
+  List<Trash> get objects => throw _privateConstructorUsedError;
   @override
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;

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

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

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

@@ -5,17 +5,20 @@ import 'package:app_flowy/workspace/application/doc/doc_edit_bloc.dart';
 import 'package:app_flowy/workspace/application/menu/menu_bloc.dart';
 import 'package:app_flowy/workspace/application/menu/menu_user_bloc.dart';
 import 'package:app_flowy/workspace/application/menu/menu_listen.dart';
+import 'package:app_flowy/workspace/application/trash/trash_bloc.dart';
 import 'package:app_flowy/workspace/application/view/view_bloc.dart';
-import 'package:app_flowy/workspace/application/view/view_edit_bloc.dart';
 import 'package:app_flowy/workspace/application/workspace/welcome_bloc.dart';
 import 'package:app_flowy/workspace/domain/i_doc.dart';
+import 'package:app_flowy/workspace/domain/i_trash.dart';
 import 'package:app_flowy/workspace/domain/i_view.dart';
 import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
 import 'package:app_flowy/workspace/infrastructure/i_app_impl.dart';
 import 'package:app_flowy/workspace/infrastructure/i_doc_impl.dart';
+import 'package:app_flowy/workspace/infrastructure/i_trash_impl.dart';
 import 'package:app_flowy/workspace/infrastructure/i_workspace_impl.dart';
 import 'package:app_flowy/workspace/infrastructure/repos/app_repo.dart';
 import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart';
+import 'package:app_flowy/workspace/infrastructure/repos/trash_repo.dart';
 import 'package:app_flowy/workspace/infrastructure/repos/view_repo.dart';
 import 'package:app_flowy/workspace/infrastructure/repos/workspace_repo.dart';
 import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
@@ -29,6 +32,12 @@ class HomeDepsResolver {
   static Future<void> resolve(GetIt getIt) async {
     //
     getIt.registerLazySingleton<HomeStackManager>(() => HomeStackManager());
+    getIt.registerFactoryParam<WelcomeBloc, UserProfile, void>(
+      (user, _) => WelcomeBloc(
+        repo: UserRepo(user: user),
+        watch: getIt<IUserListener>(param1: user),
+      ),
+    );
 
     //App
     getIt.registerFactoryParam<IApp, String, void>((appId, _) => IAppImpl(repo: AppRepository(appId: appId)));
@@ -45,6 +54,12 @@ class HomeDepsResolver {
     getIt.registerFactoryParam<IView, View, void>((view, _) => IViewImpl(repo: ViewRepository(view: view)));
     getIt.registerFactoryParam<IViewListener, View, void>(
         (view, _) => IViewListenerImpl(repo: ViewListenerRepository(view: view)));
+    getIt.registerFactoryParam<ViewBloc, View, void>(
+      (view, _) => ViewBloc(
+        iViewImpl: getIt<IView>(param1: view),
+        listener: getIt<IViewListener>(param1: view),
+      ),
+    );
 
     // Doc
     getIt.registerFactoryParam<IDoc, String, void>((docId, _) => IDocImpl(repo: DocRepository(docId: docId)));
@@ -62,27 +77,20 @@ class HomeDepsResolver {
     getIt.registerFactoryParam<MenuUserBloc, UserProfile, void>(
         (user, _) => MenuUserBloc(getIt<IUser>(param1: user), getIt<IUserListener>(param1: user)));
 
-    //
+    // App
     getIt.registerFactoryParam<AppBloc, String, void>((appId, _) => AppBloc(getIt<IApp>(param1: appId)));
     getIt.registerFactoryParam<AppListenBloc, String, void>(
         (appId, _) => AppListenBloc(getIt<IAppListenr>(param1: appId)));
 
-    getIt.registerFactoryParam<ViewBloc, View, void>(
-      (view, _) => ViewBloc(
-        iViewImpl: getIt<IView>(param1: view),
-        listener: getIt<IViewListener>(param1: view),
-      ),
-    );
-
+    // Doc
     getIt.registerFactoryParam<DocBloc, String, void>((docId, _) => DocBloc(iDocImpl: getIt<IDoc>(param1: docId)));
-
     getIt.registerFactoryParam<DocEditBloc, String, void>((docId, _) => DocEditBloc(getIt<IDoc>(param1: docId)));
 
-    getIt.registerFactoryParam<WelcomeBloc, UserProfile, void>(
-      (user, _) => WelcomeBloc(
-        repo: UserRepo(user: user),
-        watch: getIt<IUserListener>(param1: user),
-      ),
-    );
+    // trash
+    getIt.registerLazySingleton<TrashRepo>(() => TrashRepo());
+    getIt.registerLazySingleton<TrashListenerRepo>(() => TrashListenerRepo());
+    getIt.registerFactory<ITrash>(() => ITrashImpl(repo: getIt<TrashRepo>()));
+    getIt.registerFactory<ITrashListener>(() => ITrashListenerImpl(repo: getIt<TrashListenerRepo>()));
+    getIt.registerFactory<TrashBloc>(() => TrashBloc(iTrash: getIt<ITrash>(), listener: getIt<ITrashListener>()));
   }
 }

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

@@ -41,6 +41,6 @@ class IAppListenerhImpl extends IAppListenr {
 
   @override
   void start({AppViewsChangeCallback? viewsChangeCallback, AppUpdatedCallback? updatedCallback}) {
-    repo.startListen(viewsChanged: viewsChangeCallback, update: updatedCallback);
+    repo.startListening(viewsChanged: viewsChangeCallback, update: updatedCallback);
   }
 }

+ 38 - 0
app_flowy/lib/workspace/infrastructure/i_trash_impl.dart

@@ -0,0 +1,38 @@
+import 'package:app_flowy/workspace/domain/i_trash.dart';
+import 'package:app_flowy/workspace/infrastructure/repos/trash_repo.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/trash_create.pb.dart';
+
+class ITrashImpl implements ITrash {
+  TrashRepo repo;
+
+  ITrashImpl({required this.repo});
+
+  @override
+  Future<Either<List<Trash>, WorkspaceError>> readTrash() {
+    return repo.readTrash().then((result) {
+      return result.fold(
+        (repeatedTrash) => left(repeatedTrash.items),
+        (err) => right(err),
+      );
+    });
+  }
+}
+
+class ITrashListenerImpl extends ITrashListener {
+  TrashListenerRepo repo;
+  ITrashListenerImpl({
+    required this.repo,
+  });
+
+  @override
+  Future<void> stop() async {
+    await repo.close();
+  }
+
+  @override
+  void start(TrashUpdatedCallback updateCallback) {
+    repo.startListening(trashUpdated: updateCallback);
+  }
+}

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

@@ -50,7 +50,7 @@ class IUserImpl extends IUser {
 }
 
 class IUserListenerImpl extends IUserListener {
-  StreamSubscription<ObservableSubject>? _subscription;
+  StreamSubscription<SubscribeObject>? _subscription;
   WorkspacesUpdatedCallback? _workspacesUpdated;
   AuthChangedCallback? _authChanged;
   UserProfileUpdateCallback? _profileUpdated;
@@ -68,7 +68,7 @@ class IUserListenerImpl extends IUserListener {
   void start() {
     _workspaceParser = WorkspaceNotificationParser(id: _user.token, callback: _NotificationCallback);
 
-    _userParser = UserNotificationParser(id: _user.token, callback: _userObservableCallback);
+    _userParser = UserNotificationParser(id: _user.token, callback: _UserNotificationCallback);
 
     _subscription = RustStreamReceiver.listen((observable) {
       _workspaceParser.parse(observable);
@@ -96,11 +96,11 @@ class IUserListenerImpl extends IUserListener {
     _workspacesUpdated = workspacesCallback;
   }
 
-  void _NotificationCallback(Notification ty, Either<Uint8List, WorkspaceError> result) {
+  void _NotificationCallback(WorkspaceNotification ty, Either<Uint8List, WorkspaceError> result) {
     switch (ty) {
-      case Notification.UserCreateWorkspace:
-      case Notification.UserDeleteWorkspace:
-      case Notification.WorkspaceListUpdated:
+      case WorkspaceNotification.UserCreateWorkspace:
+      case WorkspaceNotification.UserDeleteWorkspace:
+      case WorkspaceNotification.WorkspaceListUpdated:
         if (_workspacesUpdated != null) {
           result.fold(
             (payload) {
@@ -111,7 +111,7 @@ class IUserListenerImpl extends IUserListener {
           );
         }
         break;
-      case Notification.UserUnauthorized:
+      case WorkspaceNotification.UserUnauthorized:
         if (_authChanged != null) {
           result.fold(
             (_) {},
@@ -124,9 +124,9 @@ class IUserListenerImpl extends IUserListener {
     }
   }
 
-  void _userObservableCallback(user.UserObservable ty, Either<Uint8List, UserError> result) {
+  void _UserNotificationCallback(user.UserNotification ty, Either<Uint8List, UserError> result) {
     switch (ty) {
-      case user.UserObservable.UserUnauthorized:
+      case user.UserNotification.UserUnauthorized:
         if (_profileUpdated != null) {
           result.fold(
             (payload) {

+ 1 - 1
app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart

@@ -36,7 +36,7 @@ class IWorkspaceListenerImpl extends IWorkspaceListener {
 
   @override
   void start({WorkspaceCreateAppCallback? addAppCallback, WorkspaceUpdatedCallback? updatedCallback}) {
-    repo.startListen(createApp: addAppCallback, update: updatedCallback);
+    repo.startListening(createApp: addAppCallback, update: updatedCallback);
   }
 
   @override

+ 8 - 8
app_flowy/lib/workspace/infrastructure/repos/app_repo.dart

@@ -53,26 +53,26 @@ class AppRepository {
 }
 
 class AppListenerRepository {
-  StreamSubscription<ObservableSubject>? _subscription;
+  StreamSubscription<SubscribeObject>? _subscription;
   AppViewsChangeCallback? _viewsChanged;
   AppUpdatedCallback? _update;
-  late WorkspaceNotificationParser _extractor;
+  late WorkspaceNotificationParser _parser;
   String appId;
 
   AppListenerRepository({
     required this.appId,
   });
 
-  void startListen({AppViewsChangeCallback? viewsChanged, AppUpdatedCallback? update}) {
+  void startListening({AppViewsChangeCallback? viewsChanged, AppUpdatedCallback? update}) {
     _viewsChanged = viewsChanged;
     _update = update;
-    _extractor = WorkspaceNotificationParser(id: appId, callback: _bservableCallback);
-    _subscription = RustStreamReceiver.listen((observable) => _extractor.parse(observable));
+    _parser = WorkspaceNotificationParser(id: appId, callback: _bservableCallback);
+    _subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
   }
 
-  void _bservableCallback(Notification ty, Either<Uint8List, WorkspaceError> result) {
+  void _bservableCallback(WorkspaceNotification ty, Either<Uint8List, WorkspaceError> result) {
     switch (ty) {
-      case Notification.AppViewsChanged:
+      case WorkspaceNotification.AppViewsChanged:
         if (_viewsChanged != null) {
           result.fold(
             (payload) {
@@ -83,7 +83,7 @@ class AppListenerRepository {
           );
         }
         break;
-      case Notification.AppUpdated:
+      case WorkspaceNotification.AppUpdated:
         if (_update != null) {
           result.fold(
             (payload) {

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

@@ -16,17 +16,16 @@ class DocRepository {
     return WorkspaceEventOpenView(request).send();
   }
 
-  Future<Either<DocDelta, WorkspaceError>> composeDelta(
-      {required String data}) {
+  Future<Either<DocDelta, WorkspaceError>> composeDelta({required String data}) {
     final request = DocDelta.create()
       ..docId = docId
       ..data = data;
     return WorkspaceEventApplyDocDelta(request).send();
   }
 
-  Future<Either<Unit, WorkspaceError>> closeDoc(
-      {String? name, String? desc, String? text}) {
+  Future<Either<Unit, WorkspaceError>> closeDoc({String? name, String? desc, String? text}) {
     Log.error('Close the doc');
+
     return Future(() {
       return left(unit);
     });

+ 15 - 13
app_flowy/lib/workspace/infrastructure/repos/helper.dart

@@ -5,41 +5,43 @@ import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/observable.pb.dart';
 import 'package:dartz/dartz.dart';
 
-typedef UserObservableCallback = void Function(UserObservable, Either<Uint8List, UserError>);
+typedef UserNotificationCallback = void Function(UserNotification, Either<Uint8List, UserError>);
 
-class UserNotificationParser extends NotificationParser<UserObservable, UserError> {
-  UserNotificationParser({required String id, required UserObservableCallback callback})
+class UserNotificationParser extends NotificationParser<UserNotification, UserError> {
+  UserNotificationParser({required String id, required UserNotificationCallback callback})
       : super(
           id: id,
           callback: callback,
-          tyParser: (ty) => UserObservable.valueOf(ty),
+          tyParser: (ty) => UserNotification.valueOf(ty),
           errorParser: (bytes) => UserError.fromBuffer(bytes),
         );
 }
 
-typedef NotificationCallback = void Function(Notification, Either<Uint8List, WorkspaceError>);
+typedef NotificationCallback = void Function(WorkspaceNotification, Either<Uint8List, WorkspaceError>);
 
-class WorkspaceNotificationParser extends NotificationParser<Notification, WorkspaceError> {
-  WorkspaceNotificationParser({required String id, required NotificationCallback callback})
+class WorkspaceNotificationParser extends NotificationParser<WorkspaceNotification, WorkspaceError> {
+  WorkspaceNotificationParser({String? id, required NotificationCallback callback})
       : super(
           id: id,
           callback: callback,
-          tyParser: (ty) => Notification.valueOf(ty),
+          tyParser: (ty) => WorkspaceNotification.valueOf(ty),
           errorParser: (bytes) => WorkspaceError.fromBuffer(bytes),
         );
 }
 
 class NotificationParser<T, E> {
-  String id;
+  String? id;
   void Function(T, Either<Uint8List, E>) callback;
 
   T? Function(int) tyParser;
   E Function(Uint8List) errorParser;
 
-  NotificationParser({required this.id, required this.callback, required this.errorParser, required this.tyParser});
-  void parse(ObservableSubject subject) {
-    if (subject.id != id) {
-      return;
+  NotificationParser({this.id, required this.callback, required this.errorParser, required this.tyParser});
+  void parse(SubscribeObject subject) {
+    if (id != null) {
+      if (subject.id != id) {
+        return;
+      }
     }
 
     final ty = tyParser(subject.ty);

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

@@ -1,9 +1,15 @@
 import 'dart:async';
+import 'dart:typed_data';
+import 'package:app_flowy/workspace/domain/i_trash.dart';
+import 'package:app_flowy/workspace/infrastructure/repos/helper.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
+import 'package:flowy_sdk/protobuf/flowy-dart-notify/subject.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/observable.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/trash_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/trash_delete.pb.dart';
+import 'package:flowy_sdk/rust_stream.dart';
 
 class TrashRepo {
   Future<Either<RepeatedTrash, WorkspaceError>> readTrash() {
@@ -21,3 +27,37 @@ class TrashRepo {
     return WorkspaceEventDeleteTrash(id).send();
   }
 }
+
+class TrashListenerRepo {
+  StreamSubscription<SubscribeObject>? _subscription;
+  TrashUpdatedCallback? _trashUpdated;
+  late WorkspaceNotificationParser _parser;
+
+  void startListening({TrashUpdatedCallback? trashUpdated}) {
+    _trashUpdated = trashUpdated;
+    _parser = WorkspaceNotificationParser(callback: _bservableCallback);
+    _subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
+  }
+
+  void _bservableCallback(WorkspaceNotification ty, Either<Uint8List, WorkspaceError> result) {
+    switch (ty) {
+      case WorkspaceNotification.TrashUpdated:
+        if (_trashUpdated != null) {
+          result.fold(
+            (payload) {
+              final repeatedTrash = RepeatedTrash.fromBuffer(payload);
+              _trashUpdated!(left(repeatedTrash.items));
+            },
+            (error) => _trashUpdated!(right(error)),
+          );
+        }
+        break;
+      default:
+        break;
+    }
+  }
+
+  Future<void> close() async {
+    await _subscription?.cancel();
+  }
+}

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

@@ -51,9 +51,9 @@ class ViewRepository {
 }
 
 class ViewListenerRepository {
-  StreamSubscription<ObservableSubject>? _subscription;
+  StreamSubscription<SubscribeObject>? _subscription;
   ViewUpdatedCallback? _update;
-  late WorkspaceNotificationParser _extractor;
+  late WorkspaceNotificationParser _parser;
   View view;
 
   ViewListenerRepository({
@@ -64,19 +64,19 @@ class ViewListenerRepository {
     ViewUpdatedCallback? update,
   }) {
     _update = update;
-    _extractor = WorkspaceNotificationParser(
+    _parser = WorkspaceNotificationParser(
       id: view.id,
       callback: (ty, result) {
         _handleObservableType(ty, result);
       },
     );
 
-    _subscription = RustStreamReceiver.listen((observable) => _extractor.parse(observable));
+    _subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
   }
 
-  void _handleObservableType(Notification ty, Either<Uint8List, WorkspaceError> result) {
+  void _handleObservableType(WorkspaceNotification ty, Either<Uint8List, WorkspaceError> result) {
     switch (ty) {
-      case Notification.ViewUpdated:
+      case WorkspaceNotification.ViewUpdated:
         if (_update != null) {
           result.fold(
             (payload) {

+ 9 - 9
app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart

@@ -63,11 +63,11 @@ class WorkspaceRepo {
 }
 
 class WorkspaceListenerRepo {
-  StreamSubscription<ObservableSubject>? _subscription;
+  StreamSubscription<SubscribeObject>? _subscription;
   WorkspaceCreateAppCallback? _createApp;
   WorkspaceDeleteAppCallback? _deleteApp;
   WorkspaceUpdatedCallback? _update;
-  late WorkspaceNotificationParser _extractor;
+  late WorkspaceNotificationParser _parser;
   final UserProfile user;
   final String workspaceId;
 
@@ -76,7 +76,7 @@ class WorkspaceListenerRepo {
     required this.workspaceId,
   });
 
-  void startListen({
+  void startListening({
     WorkspaceCreateAppCallback? createApp,
     WorkspaceDeleteAppCallback? deleteApp,
     WorkspaceUpdatedCallback? update,
@@ -85,19 +85,19 @@ class WorkspaceListenerRepo {
     _deleteApp = deleteApp;
     _update = update;
 
-    _extractor = WorkspaceNotificationParser(
+    _parser = WorkspaceNotificationParser(
       id: workspaceId,
       callback: (ty, result) {
         _handleObservableType(ty, result);
       },
     );
 
-    _subscription = RustStreamReceiver.listen((observable) => _extractor.parse(observable));
+    _subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
   }
 
-  void _handleObservableType(Notification ty, Either<Uint8List, WorkspaceError> result) {
+  void _handleObservableType(WorkspaceNotification ty, Either<Uint8List, WorkspaceError> result) {
     switch (ty) {
-      case Notification.WorkspaceUpdated:
+      case WorkspaceNotification.WorkspaceUpdated:
         if (_update != null) {
           result.fold(
             (payload) {
@@ -108,7 +108,7 @@ class WorkspaceListenerRepo {
           );
         }
         break;
-      case Notification.WorkspaceCreateApp:
+      case WorkspaceNotification.WorkspaceCreateApp:
         if (_createApp != null) {
           result.fold(
             (payload) => _createApp!(
@@ -118,7 +118,7 @@ class WorkspaceListenerRepo {
           );
         }
         break;
-      case Notification.WorkspaceDeleteApp:
+      case WorkspaceNotification.WorkspaceDeleteApp:
         if (_deleteApp != null) {
           result.fold(
             (payload) => _deleteApp!(

+ 37 - 32
app_flowy/lib/workspace/presentation/stack_page/trash/trash_page.dart

@@ -1,14 +1,19 @@
+import 'package:app_flowy/startup/startup.dart';
+import 'package:app_flowy/workspace/application/trash/trash_bloc.dart';
 import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
+import 'package:app_flowy/workspace/presentation/stack_page/trash/widget/trash_cell.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 
+import 'widget/trash_header.dart';
+
 class TrashStackContext extends HomeStackContext {
   @override
   String get identifier => "TrashStackContext";
@@ -24,7 +29,7 @@ class TrashStackContext extends HomeStackContext {
 
   @override
   Widget render() {
-    return const TrashStackPage();
+    return const TrashStackPage(key: ObjectKey('TrashStackPage'));
   }
 
   @override
@@ -46,7 +51,16 @@ class _TrashStackPageState extends State<TrashStackPage> {
       child: Column(
         children: [
           _renderTopBar(theme),
-          _renderTrashList(context, theme),
+          const VSpace(32),
+          Expanded(
+            child: CustomScrollView(
+              controller: ScrollController(),
+              slivers: [
+                _renderListHeader(context),
+                _renderListBody(context),
+              ],
+            ),
+          ),
         ],
         mainAxisAlignment: MainAxisAlignment.start,
       ).padding(horizontal: 80, vertical: 48),
@@ -84,44 +98,35 @@ class _TrashStackPageState extends State<TrashStackPage> {
     );
   }
 
-  Widget _renderTrashList(BuildContext context, AppTheme theme) {
-    return Expanded(
-      child: CustomScrollView(
-        physics: StyledScrollPhysics(),
-        slivers: [
-          _renderListHeader(context),
-          _renderListBody(context),
-        ],
-      ),
-    );
-  }
-
   Widget _renderListHeader(BuildContext context) {
-    return const SliverAppBar(
-      automaticallyImplyLeading: false,
-      backgroundColor: Colors.green,
-      title: Text('Have a nice day'),
+    return SliverPersistentHeader(
+      delegate: TrashHeaderDelegate(),
       floating: true,
+      pinned: true,
     );
   }
 
   Widget _renderListBody(BuildContext context) {
-    return SliverList(
-      delegate: SliverChildBuilderDelegate(
-        (BuildContext context, int index) {
-          return Card(
-            child: Container(
-              color: Colors.blue[100 * (index % 9 + 1)],
-              height: 80,
-              alignment: Alignment.center,
-              child: Text(
-                "Item $index",
-                style: const TextStyle(fontSize: 30),
-              ),
+    return BlocProvider(
+      create: (context) => getIt<TrashBloc>()..add(const TrashEvent.initial()),
+      child: BlocBuilder<TrashBloc, TrashState>(
+        builder: (context, state) {
+          return SliverList(
+            delegate: SliverChildBuilderDelegate(
+              (BuildContext context, int index) {
+                return SizedBox(
+                  height: 42,
+                  child: TrashCell(
+                    object: state.objects[index],
+                    onRestore: () {},
+                    onDelete: () {},
+                  ),
+                );
+              },
+              childCount: state.objects.length,
             ),
           );
         },
-        childCount: 3,
       ),
     );
   }

+ 0 - 0
app_flowy/lib/workspace/presentation/stack_page/trash/widget/list_body.dart


+ 0 - 1
app_flowy/lib/workspace/presentation/stack_page/trash/widget/list_header.dart

@@ -1 +0,0 @@
-

+ 1 - 1
app_flowy/lib/workspace/presentation/stack_page/trash/widget/sizes.dart

@@ -1,4 +1,4 @@
-class TrashListSizes {
+class TrashSizes {
   static double scale = 1;
   static double get fileNameWidth => 320 * scale;
   static double get lashModifyWidth => 230 * scale;

+ 38 - 0
app_flowy/lib/workspace/presentation/stack_page/trash/widget/trash_cell.dart

@@ -0,0 +1,38 @@
+import 'package:flowy_infra/image.dart';
+import 'package:flowy_infra_ui/style_widget/icon_button.dart';
+import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flowy_infra_ui/widget/spacing.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/trash_create.pb.dart';
+import 'package:flutter/material.dart';
+
+import 'sizes.dart';
+
+class TrashCell extends StatelessWidget {
+  final VoidCallback onRestore;
+  final VoidCallback onDelete;
+  final Trash object;
+  const TrashCell({required this.object, required this.onRestore, required this.onDelete, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      children: [
+        SizedBox(width: TrashSizes.fileNameWidth, child: FlowyText(object.name, fontSize: 12)),
+        SizedBox(width: TrashSizes.lashModifyWidth, child: FlowyText("${object.modifiedTime}", fontSize: 12)),
+        SizedBox(width: TrashSizes.createTimeWidth, child: FlowyText("${object.createTime}", fontSize: 12)),
+        const Spacer(),
+        FlowyIconButton(
+          width: 16,
+          onPressed: onRestore,
+          icon: svg("editor/restore"),
+        ),
+        const HSpace(20),
+        FlowyIconButton(
+          width: 16,
+          onPressed: onDelete,
+          icon: svg("editor/delete"),
+        ),
+      ],
+    );
+  }
+}

+ 71 - 0
app_flowy/lib/workspace/presentation/stack_page/trash/widget/trash_header.dart

@@ -0,0 +1,71 @@
+import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+import 'sizes.dart';
+
+class TrashHeaderDelegate extends SliverPersistentHeaderDelegate {
+  TrashHeaderDelegate();
+
+  @override
+  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
+    return TrashHeader();
+  }
+
+  @override
+  double get maxExtent => 60;
+
+  @override
+  double get minExtent => 60;
+
+  @override
+  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
+    return false;
+  }
+}
+
+class TrashHeaderItem {
+  double width;
+  String title;
+
+  TrashHeaderItem({required this.width, required this.title});
+}
+
+class TrashHeader extends StatelessWidget {
+  final List<TrashHeaderItem> items = [
+    TrashHeaderItem(title: 'File name', width: TrashSizes.fileNameWidth),
+    TrashHeaderItem(title: 'Last modified', width: TrashSizes.lashModifyWidth),
+    TrashHeaderItem(title: 'Created', width: TrashSizes.createTimeWidth),
+  ];
+
+  TrashHeader({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = context.watch<AppTheme>();
+    final headerItems = List<Widget>.empty(growable: true);
+    items.asMap().forEach((index, item) {
+      headerItems.add(
+        SizedBox(
+          width: item.width,
+          child: FlowyText(
+            item.title,
+            fontSize: 12,
+            color: theme.shader3,
+          ),
+        ),
+      );
+    });
+
+    return Container(
+      color: Colors.white,
+      child: Row(
+        crossAxisAlignment: CrossAxisAlignment.stretch,
+        children: [
+          ...headerItems,
+        ],
+      ),
+    );
+  }
+}

+ 1 - 1
app_flowy/lib/workspace/presentation/widgets/menu/widget/menu_trash.dart

@@ -1,6 +1,6 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
-import 'package:app_flowy/workspace/presentation/stack_page/trash_page.dart';
+import 'package:app_flowy/workspace/presentation/stack_page/trash/trash_page.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';

+ 8 - 5
app_flowy/packages/flowy_infra_ui/lib/style_widget/text.dart

@@ -7,25 +7,27 @@ class FlowyText extends StatelessWidget {
   final TextOverflow overflow;
   final double fontSize;
   final FontWeight fontWeight;
+  final Color? color;
   const FlowyText(
     this.title, {
     Key? key,
     this.overflow = TextOverflow.ellipsis,
     this.fontSize = 16,
-    this.fontWeight = FontWeight.w500,
+    this.fontWeight = FontWeight.w400,
+    this.color,
   }) : super(key: key);
 
-  const FlowyText.semibold(this.title, {Key? key, this.fontSize = 16, TextOverflow? overflow})
+  const FlowyText.semibold(this.title, {Key? key, this.fontSize = 16, TextOverflow? overflow, this.color})
       : fontWeight = FontWeight.w600,
         overflow = overflow ?? TextOverflow.ellipsis,
         super(key: key);
 
-  const FlowyText.medium(this.title, {Key? key, this.fontSize = 16, TextOverflow? overflow})
+  const FlowyText.medium(this.title, {Key? key, this.fontSize = 16, TextOverflow? overflow, this.color})
       : fontWeight = FontWeight.w500,
         overflow = overflow ?? TextOverflow.ellipsis,
         super(key: key);
 
-  const FlowyText.regular(this.title, {Key? key, this.fontSize = 16, TextOverflow? overflow})
+  const FlowyText.regular(this.title, {Key? key, this.fontSize = 16, TextOverflow? overflow, this.color})
       : fontWeight = FontWeight.w400,
         overflow = overflow ?? TextOverflow.ellipsis,
         super(key: key);
@@ -33,11 +35,12 @@ class FlowyText extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
+    final textColor = color ?? theme.shader1;
     return Text(title,
         overflow: overflow,
         softWrap: false,
         style: TextStyle(
-          color: theme.shader1,
+          color: textColor,
           fontWeight: fontWeight,
           fontSize: fontSize + 2,
         ));

+ 23 - 23
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-dart-notify/subject.pb.dart

@@ -9,26 +9,26 @@ import 'dart:core' as $core;
 
 import 'package:protobuf/protobuf.dart' as $pb;
 
-enum ObservableSubject_OneOfPayload {
+enum SubscribeObject_OneOfPayload {
   payload, 
   notSet
 }
 
-enum ObservableSubject_OneOfError {
+enum SubscribeObject_OneOfError {
   error, 
   notSet
 }
 
-class ObservableSubject extends $pb.GeneratedMessage {
-  static const $core.Map<$core.int, ObservableSubject_OneOfPayload> _ObservableSubject_OneOfPayloadByTag = {
-    4 : ObservableSubject_OneOfPayload.payload,
-    0 : ObservableSubject_OneOfPayload.notSet
+class SubscribeObject extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, SubscribeObject_OneOfPayload> _SubscribeObject_OneOfPayloadByTag = {
+    4 : SubscribeObject_OneOfPayload.payload,
+    0 : SubscribeObject_OneOfPayload.notSet
   };
-  static const $core.Map<$core.int, ObservableSubject_OneOfError> _ObservableSubject_OneOfErrorByTag = {
-    5 : ObservableSubject_OneOfError.error,
-    0 : ObservableSubject_OneOfError.notSet
+  static const $core.Map<$core.int, SubscribeObject_OneOfError> _SubscribeObject_OneOfErrorByTag = {
+    5 : SubscribeObject_OneOfError.error,
+    0 : SubscribeObject_OneOfError.notSet
   };
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ObservableSubject', createEmptyInstance: create)
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SubscribeObject', createEmptyInstance: create)
     ..oo(0, [4])
     ..oo(1, [5])
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'source')
@@ -39,8 +39,8 @@ class ObservableSubject extends $pb.GeneratedMessage {
     ..hasRequiredFields = false
   ;
 
-  ObservableSubject._() : super();
-  factory ObservableSubject({
+  SubscribeObject._() : super();
+  factory SubscribeObject({
     $core.String? source,
     $core.int? ty,
     $core.String? id,
@@ -65,31 +65,31 @@ class ObservableSubject extends $pb.GeneratedMessage {
     }
     return _result;
   }
-  factory ObservableSubject.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory ObservableSubject.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory SubscribeObject.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory SubscribeObject.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')
-  ObservableSubject clone() => ObservableSubject()..mergeFromMessage(this);
+  SubscribeObject clone() => SubscribeObject()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  ObservableSubject copyWith(void Function(ObservableSubject) updates) => super.copyWith((message) => updates(message as ObservableSubject)) as ObservableSubject; // ignore: deprecated_member_use
+  SubscribeObject copyWith(void Function(SubscribeObject) updates) => super.copyWith((message) => updates(message as SubscribeObject)) as SubscribeObject; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static ObservableSubject create() => ObservableSubject._();
-  ObservableSubject createEmptyInstance() => create();
-  static $pb.PbList<ObservableSubject> createRepeated() => $pb.PbList<ObservableSubject>();
+  static SubscribeObject create() => SubscribeObject._();
+  SubscribeObject createEmptyInstance() => create();
+  static $pb.PbList<SubscribeObject> createRepeated() => $pb.PbList<SubscribeObject>();
   @$core.pragma('dart2js:noInline')
-  static ObservableSubject getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ObservableSubject>(create);
-  static ObservableSubject? _defaultInstance;
+  static SubscribeObject getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SubscribeObject>(create);
+  static SubscribeObject? _defaultInstance;
 
-  ObservableSubject_OneOfPayload whichOneOfPayload() => _ObservableSubject_OneOfPayloadByTag[$_whichOneof(0)]!;
+  SubscribeObject_OneOfPayload whichOneOfPayload() => _SubscribeObject_OneOfPayloadByTag[$_whichOneof(0)]!;
   void clearOneOfPayload() => clearField($_whichOneof(0));
 
-  ObservableSubject_OneOfError whichOneOfError() => _ObservableSubject_OneOfErrorByTag[$_whichOneof(1)]!;
+  SubscribeObject_OneOfError whichOneOfError() => _SubscribeObject_OneOfErrorByTag[$_whichOneof(1)]!;
   void clearOneOfError() => clearField($_whichOneof(1));
 
   @$pb.TagNumber(1)

+ 5 - 5
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-dart-notify/subject.pbjson.dart

@@ -8,9 +8,9 @@
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
 import 'dart:typed_data' as $typed_data;
-@$core.Deprecated('Use observableSubjectDescriptor instead')
-const ObservableSubject$json = const {
-  '1': 'ObservableSubject',
+@$core.Deprecated('Use subscribeObjectDescriptor instead')
+const SubscribeObject$json = const {
+  '1': 'SubscribeObject',
   '2': const [
     const {'1': 'source', '3': 1, '4': 1, '5': 9, '10': 'source'},
     const {'1': 'ty', '3': 2, '4': 1, '5': 5, '10': 'ty'},
@@ -24,5 +24,5 @@ const ObservableSubject$json = const {
   ],
 };
 
-/// Descriptor for `ObservableSubject`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List observableSubjectDescriptor = $convert.base64Decode('ChFPYnNlcnZhYmxlU3ViamVjdBIWCgZzb3VyY2UYASABKAlSBnNvdXJjZRIOCgJ0eRgCIAEoBVICdHkSDgoCaWQYAyABKAlSAmlkEhoKB3BheWxvYWQYBCABKAxIAFIHcGF5bG9hZBIWCgVlcnJvchgFIAEoDEgBUgVlcnJvckIQCg5vbmVfb2ZfcGF5bG9hZEIOCgxvbmVfb2ZfZXJyb3I=');
+/// Descriptor for `SubscribeObject`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List subscribeObjectDescriptor = $convert.base64Decode('Cg9TdWJzY3JpYmVPYmplY3QSFgoGc291cmNlGAEgASgJUgZzb3VyY2USDgoCdHkYAiABKAVSAnR5Eg4KAmlkGAMgASgJUgJpZBIaCgdwYXlsb2FkGAQgASgMSABSB3BheWxvYWQSFgoFZXJyb3IYBSABKAxIAVIFZXJyb3JCEAoOb25lX29mX3BheWxvYWRCDgoMb25lX29mX2Vycm9y');

+ 9 - 9
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/observable.pbenum.dart

@@ -9,22 +9,22 @@
 import 'dart:core' as $core;
 import 'package:protobuf/protobuf.dart' as $pb;
 
-class UserObservable extends $pb.ProtobufEnum {
-  static const UserObservable Unknown = UserObservable._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
-  static const UserObservable UserAuthChanged = UserObservable._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserAuthChanged');
-  static const UserObservable UserProfileUpdated = UserObservable._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserProfileUpdated');
-  static const UserObservable UserUnauthorized = UserObservable._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserUnauthorized');
+class UserNotification extends $pb.ProtobufEnum {
+  static const UserNotification Unknown = UserNotification._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
+  static const UserNotification UserAuthChanged = UserNotification._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserAuthChanged');
+  static const UserNotification UserProfileUpdated = UserNotification._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserProfileUpdated');
+  static const UserNotification UserUnauthorized = UserNotification._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserUnauthorized');
 
-  static const $core.List<UserObservable> values = <UserObservable> [
+  static const $core.List<UserNotification> values = <UserNotification> [
     Unknown,
     UserAuthChanged,
     UserProfileUpdated,
     UserUnauthorized,
   ];
 
-  static final $core.Map<$core.int, UserObservable> _byValue = $pb.ProtobufEnum.initByValue(values);
-  static UserObservable? valueOf($core.int value) => _byValue[value];
+  static final $core.Map<$core.int, UserNotification> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static UserNotification? valueOf($core.int value) => _byValue[value];
 
-  const UserObservable._($core.int v, $core.String n) : super(v, n);
+  const UserNotification._($core.int v, $core.String n) : super(v, n);
 }
 

+ 5 - 5
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/observable.pbjson.dart

@@ -8,9 +8,9 @@
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
 import 'dart:typed_data' as $typed_data;
-@$core.Deprecated('Use userObservableDescriptor instead')
-const UserObservable$json = const {
-  '1': 'UserObservable',
+@$core.Deprecated('Use userNotificationDescriptor instead')
+const UserNotification$json = const {
+  '1': 'UserNotification',
   '2': const [
     const {'1': 'Unknown', '2': 0},
     const {'1': 'UserAuthChanged', '2': 1},
@@ -19,5 +19,5 @@ const UserObservable$json = const {
   ],
 };
 
-/// Descriptor for `UserObservable`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List userObservableDescriptor = $convert.base64Decode('Cg5Vc2VyT2JzZXJ2YWJsZRILCgdVbmtub3duEAASEwoPVXNlckF1dGhDaGFuZ2VkEAESFgoSVXNlclByb2ZpbGVVcGRhdGVkEAISFAoQVXNlclVuYXV0aG9yaXplZBAD');
+/// Descriptor for `UserNotification`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List userNotificationDescriptor = $convert.base64Decode('ChBVc2VyTm90aWZpY2F0aW9uEgsKB1Vua25vd24QABITCg9Vc2VyQXV0aENoYW5nZWQQARIWChJVc2VyUHJvZmlsZVVwZGF0ZWQQAhIUChBVc2VyVW5hdXRob3JpemVkEAM=');

+ 18 - 16
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbenum.dart

@@ -9,20 +9,21 @@
 import 'dart:core' as $core;
 import 'package:protobuf/protobuf.dart' as $pb;
 
-class Notification extends $pb.ProtobufEnum {
-  static const Notification Unknown = Notification._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
-  static const Notification UserCreateWorkspace = Notification._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserCreateWorkspace');
-  static const Notification UserDeleteWorkspace = Notification._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDeleteWorkspace');
-  static const Notification WorkspaceUpdated = Notification._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceUpdated');
-  static const Notification WorkspaceCreateApp = Notification._(13, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceCreateApp');
-  static const Notification WorkspaceDeleteApp = Notification._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceDeleteApp');
-  static const Notification WorkspaceListUpdated = Notification._(15, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceListUpdated');
-  static const Notification AppUpdated = Notification._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppUpdated');
-  static const Notification AppViewsChanged = Notification._(24, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppViewsChanged');
-  static const Notification ViewUpdated = Notification._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewUpdated');
-  static const Notification UserUnauthorized = Notification._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserUnauthorized');
+class WorkspaceNotification extends $pb.ProtobufEnum {
+  static const WorkspaceNotification Unknown = WorkspaceNotification._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
+  static const WorkspaceNotification UserCreateWorkspace = WorkspaceNotification._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserCreateWorkspace');
+  static const WorkspaceNotification UserDeleteWorkspace = WorkspaceNotification._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDeleteWorkspace');
+  static const WorkspaceNotification WorkspaceUpdated = WorkspaceNotification._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceUpdated');
+  static const WorkspaceNotification WorkspaceCreateApp = WorkspaceNotification._(13, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceCreateApp');
+  static const WorkspaceNotification WorkspaceDeleteApp = WorkspaceNotification._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceDeleteApp');
+  static const WorkspaceNotification WorkspaceListUpdated = WorkspaceNotification._(15, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceListUpdated');
+  static const WorkspaceNotification AppUpdated = WorkspaceNotification._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppUpdated');
+  static const WorkspaceNotification AppViewsChanged = WorkspaceNotification._(24, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppViewsChanged');
+  static const WorkspaceNotification ViewUpdated = WorkspaceNotification._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewUpdated');
+  static const WorkspaceNotification UserUnauthorized = WorkspaceNotification._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserUnauthorized');
+  static const WorkspaceNotification TrashUpdated = WorkspaceNotification._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'TrashUpdated');
 
-  static const $core.List<Notification> values = <Notification> [
+  static const $core.List<WorkspaceNotification> values = <WorkspaceNotification> [
     Unknown,
     UserCreateWorkspace,
     UserDeleteWorkspace,
@@ -34,11 +35,12 @@ class Notification extends $pb.ProtobufEnum {
     AppViewsChanged,
     ViewUpdated,
     UserUnauthorized,
+    TrashUpdated,
   ];
 
-  static final $core.Map<$core.int, Notification> _byValue = $pb.ProtobufEnum.initByValue(values);
-  static Notification? valueOf($core.int value) => _byValue[value];
+  static final $core.Map<$core.int, WorkspaceNotification> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static WorkspaceNotification? valueOf($core.int value) => _byValue[value];
 
-  const Notification._($core.int v, $core.String n) : super(v, n);
+  const WorkspaceNotification._($core.int v, $core.String n) : super(v, n);
 }
 

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

@@ -8,9 +8,9 @@
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
 import 'dart:typed_data' as $typed_data;
-@$core.Deprecated('Use notificationDescriptor instead')
-const Notification$json = const {
-  '1': 'Notification',
+@$core.Deprecated('Use workspaceNotificationDescriptor instead')
+const WorkspaceNotification$json = const {
+  '1': 'WorkspaceNotification',
   '2': const [
     const {'1': 'Unknown', '2': 0},
     const {'1': 'UserCreateWorkspace', '2': 10},
@@ -23,8 +23,9 @@ const Notification$json = const {
     const {'1': 'AppViewsChanged', '2': 24},
     const {'1': 'ViewUpdated', '2': 31},
     const {'1': 'UserUnauthorized', '2': 100},
+    const {'1': 'TrashUpdated', '2': 1000},
   ],
 };
 
-/// Descriptor for `Notification`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List notificationDescriptor = $convert.base64Decode('CgxOb3RpZmljYXRpb24SCwoHVW5rbm93bhAAEhcKE1VzZXJDcmVhdGVXb3Jrc3BhY2UQChIXChNVc2VyRGVsZXRlV29ya3NwYWNlEAsSFAoQV29ya3NwYWNlVXBkYXRlZBAMEhYKEldvcmtzcGFjZUNyZWF0ZUFwcBANEhYKEldvcmtzcGFjZURlbGV0ZUFwcBAOEhgKFFdvcmtzcGFjZUxpc3RVcGRhdGVkEA8SDgoKQXBwVXBkYXRlZBAVEhMKD0FwcFZpZXdzQ2hhbmdlZBAYEg8KC1ZpZXdVcGRhdGVkEB8SFAoQVXNlclVuYXV0aG9yaXplZBBk');
+/// Descriptor for `WorkspaceNotification`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List workspaceNotificationDescriptor = $convert.base64Decode('ChVXb3Jrc3BhY2VOb3RpZmljYXRpb24SCwoHVW5rbm93bhAAEhcKE1VzZXJDcmVhdGVXb3Jrc3BhY2UQChIXChNVc2VyRGVsZXRlV29ya3NwYWNlEAsSFAoQV29ya3NwYWNlVXBkYXRlZBAMEhYKEldvcmtzcGFjZUNyZWF0ZUFwcBANEhYKEldvcmtzcGFjZURlbGV0ZUFwcBAOEhgKFFdvcmtzcGFjZUxpc3RVcGRhdGVkEA8SDgoKQXBwVXBkYXRlZBAVEhMKD0FwcFZpZXdzQ2hhbmdlZBAYEg8KC1ZpZXdVcGRhdGVkEB8SFAoQVXNlclVuYXV0aG9yaXplZBBkEhEKDFRyYXNoVXBkYXRlZBDoBw==');

+ 6 - 7
app_flowy/packages/flowy_sdk/lib/rust_stream.dart

@@ -5,17 +5,17 @@ import 'dart:ffi';
 import 'package:flowy_log/flowy_log.dart';
 import 'protobuf/flowy-dart-notify/subject.pb.dart';
 
-typedef ObserverCallback = void Function(ObservableSubject observable);
+typedef ObserverCallback = void Function(SubscribeObject observable);
 
 class RustStreamReceiver {
   static RustStreamReceiver shared = RustStreamReceiver._internal();
   late RawReceivePort _ffiPort;
   late StreamController<Uint8List> _streamController;
-  late StreamController<ObservableSubject> _observableController;
+  late StreamController<SubscribeObject> _observableController;
   late StreamSubscription<Uint8List> _ffiSubscription;
 
   int get port => _ffiPort.sendPort.nativePort;
-  StreamController<ObservableSubject> get observable => _observableController;
+  StreamController<SubscribeObject> get observable => _observableController;
 
   RustStreamReceiver._internal() {
     _ffiPort = RawReceivePort();
@@ -30,17 +30,16 @@ class RustStreamReceiver {
     return shared;
   }
 
-  static listen(void Function(ObservableSubject subject) callback) {
+  static listen(void Function(SubscribeObject subject) callback) {
     RustStreamReceiver.shared.observable.stream.listen(callback);
   }
 
   void streamCallback(Uint8List bytes) {
     try {
-      final observable = ObservableSubject.fromBuffer(bytes);
+      final observable = SubscribeObject.fromBuffer(bytes);
       _observableController.add(observable);
     } catch (e, s) {
-      Log.error(
-          'RustStreamReceiver ObservableSubject deserialize error: ${e.runtimeType}');
+      Log.error('RustStreamReceiver SubscribeObject deserialize error: ${e.runtimeType}');
       Log.error('Stack trace \n $s');
       rethrow;
     }

+ 3 - 3
rust-lib/flowy-dart-notify/src/dart/stream_sender.rs

@@ -1,4 +1,4 @@
-use crate::entities::ObservableSubject;
+use crate::entities::SubscribeObject;
 use bytes::Bytes;
 use lazy_static::lazy_static;
 use std::{convert::TryInto, sync::RwLock};
@@ -21,7 +21,7 @@ impl DartStreamSender {
     }
 
     #[allow(dead_code)]
-    fn inner_post(&self, observable_subject: ObservableSubject) -> Result<(), String> {
+    fn inner_post(&self, observable_subject: SubscribeObject) -> Result<(), String> {
         match self.isolate {
             Some(ref isolate) => {
                 let bytes: Bytes = observable_subject.try_into().unwrap();
@@ -42,7 +42,7 @@ impl DartStreamSender {
         }
     }
 
-    pub fn post(_observable_subject: ObservableSubject) -> Result<(), String> {
+    pub fn post(_observable_subject: SubscribeObject) -> Result<(), String> {
         #[cfg(feature = "dart")]
         match DART_STREAM_SENDER.read() {
             Ok(stream) => stream.inner_post(_observable_subject),

+ 3 - 3
rust-lib/flowy-dart-notify/src/entities/subject.rs

@@ -2,7 +2,7 @@ use flowy_derive::ProtoBuf;
 use std::{fmt, fmt::Formatter};
 
 #[derive(Debug, Clone, ProtoBuf)]
-pub struct ObservableSubject {
+pub struct SubscribeObject {
     #[pb(index = 1)]
     pub source: String,
 
@@ -19,7 +19,7 @@ pub struct ObservableSubject {
     pub error: Option<Vec<u8>>,
 }
 
-impl std::fmt::Display for ObservableSubject {
+impl std::fmt::Display for SubscribeObject {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         let _ = f.write_str(&format!("{} changed: ", &self.source))?;
         if let Some(payload) = &self.payload {
@@ -34,7 +34,7 @@ impl std::fmt::Display for ObservableSubject {
     }
 }
 
-impl std::default::Default for ObservableSubject {
+impl std::default::Default for SubscribeObject {
     fn default() -> Self {
         Self {
             source: "".to_string(),

+ 2 - 2
rust-lib/flowy-dart-notify/src/lib.rs

@@ -4,7 +4,7 @@ pub mod dart;
 pub mod entities;
 mod protobuf;
 
-use crate::{dart::DartStreamSender, entities::ObservableSubject};
+use crate::{dart::DartStreamSender, entities::SubscribeObject};
 use flowy_dispatch::prelude::ToBytes;
 
 pub struct DartNotifyBuilder {
@@ -64,7 +64,7 @@ impl DartNotifyBuilder {
             Some(bytes) => Some(bytes.to_vec()),
         };
 
-        let subject = ObservableSubject {
+        let subject = SubscribeObject {
             source: self.source,
             ty: self.ty,
             id: self.id,

+ 57 - 57
rust-lib/flowy-dart-notify/src/protobuf/model/subject.rs

@@ -24,37 +24,37 @@
 // const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
 
 #[derive(PartialEq,Clone,Default)]
-pub struct ObservableSubject {
+pub struct SubscribeObject {
     // message fields
     pub source: ::std::string::String,
     pub ty: i32,
     pub id: ::std::string::String,
     // message oneof groups
-    pub one_of_payload: ::std::option::Option<ObservableSubject_oneof_one_of_payload>,
-    pub one_of_error: ::std::option::Option<ObservableSubject_oneof_one_of_error>,
+    pub one_of_payload: ::std::option::Option<SubscribeObject_oneof_one_of_payload>,
+    pub one_of_error: ::std::option::Option<SubscribeObject_oneof_one_of_error>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a ObservableSubject {
-    fn default() -> &'a ObservableSubject {
-        <ObservableSubject as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a SubscribeObject {
+    fn default() -> &'a SubscribeObject {
+        <SubscribeObject as ::protobuf::Message>::default_instance()
     }
 }
 
 #[derive(Clone,PartialEq,Debug)]
-pub enum ObservableSubject_oneof_one_of_payload {
+pub enum SubscribeObject_oneof_one_of_payload {
     payload(::std::vec::Vec<u8>),
 }
 
 #[derive(Clone,PartialEq,Debug)]
-pub enum ObservableSubject_oneof_one_of_error {
+pub enum SubscribeObject_oneof_one_of_error {
     error(::std::vec::Vec<u8>),
 }
 
-impl ObservableSubject {
-    pub fn new() -> ObservableSubject {
+impl SubscribeObject {
+    pub fn new() -> SubscribeObject {
         ::std::default::Default::default()
     }
 
@@ -130,7 +130,7 @@ impl ObservableSubject {
 
     pub fn get_payload(&self) -> &[u8] {
         match self.one_of_payload {
-            ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(ref v)) => v,
+            ::std::option::Option::Some(SubscribeObject_oneof_one_of_payload::payload(ref v)) => v,
             _ => &[],
         }
     }
@@ -140,24 +140,24 @@ impl ObservableSubject {
 
     pub fn has_payload(&self) -> bool {
         match self.one_of_payload {
-            ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(..)) => true,
+            ::std::option::Option::Some(SubscribeObject_oneof_one_of_payload::payload(..)) => true,
             _ => false,
         }
     }
 
     // Param is passed by value, moved
     pub fn set_payload(&mut self, v: ::std::vec::Vec<u8>) {
-        self.one_of_payload = ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(v))
+        self.one_of_payload = ::std::option::Option::Some(SubscribeObject_oneof_one_of_payload::payload(v))
     }
 
     // Mutable pointer to the field.
     pub fn mut_payload(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if let ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(_)) = self.one_of_payload {
+        if let ::std::option::Option::Some(SubscribeObject_oneof_one_of_payload::payload(_)) = self.one_of_payload {
         } else {
-            self.one_of_payload = ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(::std::vec::Vec::new()));
+            self.one_of_payload = ::std::option::Option::Some(SubscribeObject_oneof_one_of_payload::payload(::std::vec::Vec::new()));
         }
         match self.one_of_payload {
-            ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(ref mut v)) => v,
+            ::std::option::Option::Some(SubscribeObject_oneof_one_of_payload::payload(ref mut v)) => v,
             _ => panic!(),
         }
     }
@@ -166,7 +166,7 @@ impl ObservableSubject {
     pub fn take_payload(&mut self) -> ::std::vec::Vec<u8> {
         if self.has_payload() {
             match self.one_of_payload.take() {
-                ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(v)) => v,
+                ::std::option::Option::Some(SubscribeObject_oneof_one_of_payload::payload(v)) => v,
                 _ => panic!(),
             }
         } else {
@@ -179,7 +179,7 @@ impl ObservableSubject {
 
     pub fn get_error(&self) -> &[u8] {
         match self.one_of_error {
-            ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(ref v)) => v,
+            ::std::option::Option::Some(SubscribeObject_oneof_one_of_error::error(ref v)) => v,
             _ => &[],
         }
     }
@@ -189,24 +189,24 @@ impl ObservableSubject {
 
     pub fn has_error(&self) -> bool {
         match self.one_of_error {
-            ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(..)) => true,
+            ::std::option::Option::Some(SubscribeObject_oneof_one_of_error::error(..)) => true,
             _ => false,
         }
     }
 
     // Param is passed by value, moved
     pub fn set_error(&mut self, v: ::std::vec::Vec<u8>) {
-        self.one_of_error = ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(v))
+        self.one_of_error = ::std::option::Option::Some(SubscribeObject_oneof_one_of_error::error(v))
     }
 
     // Mutable pointer to the field.
     pub fn mut_error(&mut self) -> &mut ::std::vec::Vec<u8> {
-        if let ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(_)) = self.one_of_error {
+        if let ::std::option::Option::Some(SubscribeObject_oneof_one_of_error::error(_)) = self.one_of_error {
         } else {
-            self.one_of_error = ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(::std::vec::Vec::new()));
+            self.one_of_error = ::std::option::Option::Some(SubscribeObject_oneof_one_of_error::error(::std::vec::Vec::new()));
         }
         match self.one_of_error {
-            ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(ref mut v)) => v,
+            ::std::option::Option::Some(SubscribeObject_oneof_one_of_error::error(ref mut v)) => v,
             _ => panic!(),
         }
     }
@@ -215,7 +215,7 @@ impl ObservableSubject {
     pub fn take_error(&mut self) -> ::std::vec::Vec<u8> {
         if self.has_error() {
             match self.one_of_error.take() {
-                ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(v)) => v,
+                ::std::option::Option::Some(SubscribeObject_oneof_one_of_error::error(v)) => v,
                 _ => panic!(),
             }
         } else {
@@ -224,7 +224,7 @@ impl ObservableSubject {
     }
 }
 
-impl ::protobuf::Message for ObservableSubject {
+impl ::protobuf::Message for SubscribeObject {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -250,13 +250,13 @@ impl ::protobuf::Message for ObservableSubject {
                     if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
                         return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
                     }
-                    self.one_of_payload = ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(is.read_bytes()?));
+                    self.one_of_payload = ::std::option::Option::Some(SubscribeObject_oneof_one_of_payload::payload(is.read_bytes()?));
                 },
                 5 => {
                     if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
                         return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
                     }
-                    self.one_of_error = ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(is.read_bytes()?));
+                    self.one_of_error = ::std::option::Option::Some(SubscribeObject_oneof_one_of_error::error(is.read_bytes()?));
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -281,14 +281,14 @@ impl ::protobuf::Message for ObservableSubject {
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_payload {
             match v {
-                &ObservableSubject_oneof_one_of_payload::payload(ref v) => {
+                &SubscribeObject_oneof_one_of_payload::payload(ref v) => {
                     my_size += ::protobuf::rt::bytes_size(4, &v);
                 },
             };
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_error {
             match v {
-                &ObservableSubject_oneof_one_of_error::error(ref v) => {
+                &SubscribeObject_oneof_one_of_error::error(ref v) => {
                     my_size += ::protobuf::rt::bytes_size(5, &v);
                 },
             };
@@ -310,14 +310,14 @@ impl ::protobuf::Message for ObservableSubject {
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_payload {
             match v {
-                &ObservableSubject_oneof_one_of_payload::payload(ref v) => {
+                &SubscribeObject_oneof_one_of_payload::payload(ref v) => {
                     os.write_bytes(4, v)?;
                 },
             };
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_error {
             match v {
-                &ObservableSubject_oneof_one_of_error::error(ref v) => {
+                &SubscribeObject_oneof_one_of_error::error(ref v) => {
                     os.write_bytes(5, v)?;
                 },
             };
@@ -352,8 +352,8 @@ impl ::protobuf::Message for ObservableSubject {
         Self::descriptor_static()
     }
 
-    fn new() -> ObservableSubject {
-        ObservableSubject::new()
+    fn new() -> SubscribeObject {
+        SubscribeObject::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -362,44 +362,44 @@ impl ::protobuf::Message for ObservableSubject {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "source",
-                |m: &ObservableSubject| { &m.source },
-                |m: &mut ObservableSubject| { &mut m.source },
+                |m: &SubscribeObject| { &m.source },
+                |m: &mut SubscribeObject| { &mut m.source },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt32>(
                 "ty",
-                |m: &ObservableSubject| { &m.ty },
-                |m: &mut ObservableSubject| { &mut m.ty },
+                |m: &SubscribeObject| { &m.ty },
+                |m: &mut SubscribeObject| { &mut m.ty },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "id",
-                |m: &ObservableSubject| { &m.id },
-                |m: &mut ObservableSubject| { &mut m.id },
+                |m: &SubscribeObject| { &m.id },
+                |m: &mut SubscribeObject| { &mut m.id },
             ));
             fields.push(::protobuf::reflect::accessor::make_singular_bytes_accessor::<_>(
                 "payload",
-                ObservableSubject::has_payload,
-                ObservableSubject::get_payload,
+                SubscribeObject::has_payload,
+                SubscribeObject::get_payload,
             ));
             fields.push(::protobuf::reflect::accessor::make_singular_bytes_accessor::<_>(
                 "error",
-                ObservableSubject::has_error,
-                ObservableSubject::get_error,
+                SubscribeObject::has_error,
+                SubscribeObject::get_error,
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<ObservableSubject>(
-                "ObservableSubject",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SubscribeObject>(
+                "SubscribeObject",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static ObservableSubject {
-        static instance: ::protobuf::rt::LazyV2<ObservableSubject> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(ObservableSubject::new)
+    fn default_instance() -> &'static SubscribeObject {
+        static instance: ::protobuf::rt::LazyV2<SubscribeObject> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(SubscribeObject::new)
     }
 }
 
-impl ::protobuf::Clear for ObservableSubject {
+impl ::protobuf::Clear for SubscribeObject {
     fn clear(&mut self) {
         self.source.clear();
         self.ty = 0;
@@ -410,26 +410,26 @@ impl ::protobuf::Clear for ObservableSubject {
     }
 }
 
-impl ::std::fmt::Debug for ObservableSubject {
+impl ::std::fmt::Debug for SubscribeObject {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for ObservableSubject {
+impl ::protobuf::reflect::ProtobufValue for SubscribeObject {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\rsubject.proto\"\xa1\x01\n\x11ObservableSubject\x12\x16\n\x06source\
-    \x18\x01\x20\x01(\tR\x06source\x12\x0e\n\x02ty\x18\x02\x20\x01(\x05R\x02\
-    ty\x12\x0e\n\x02id\x18\x03\x20\x01(\tR\x02id\x12\x1a\n\x07payload\x18\
-    \x04\x20\x01(\x0cH\0R\x07payload\x12\x16\n\x05error\x18\x05\x20\x01(\x0c\
-    H\x01R\x05errorB\x10\n\x0eone_of_payloadB\x0e\n\x0cone_of_errorJ\xf3\x02\
+    \n\rsubject.proto\"\x9f\x01\n\x0fSubscribeObject\x12\x16\n\x06source\x18\
+    \x01\x20\x01(\tR\x06source\x12\x0e\n\x02ty\x18\x02\x20\x01(\x05R\x02ty\
+    \x12\x0e\n\x02id\x18\x03\x20\x01(\tR\x02id\x12\x1a\n\x07payload\x18\x04\
+    \x20\x01(\x0cH\0R\x07payload\x12\x16\n\x05error\x18\x05\x20\x01(\x0cH\
+    \x01R\x05errorB\x10\n\x0eone_of_payloadB\x0e\n\x0cone_of_errorJ\xf3\x02\
     \n\x06\x12\x04\0\0\x08\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\
+    \0\x12\x04\x02\0\x08\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x17\n\x0b\n\
     \x04\x04\0\x02\0\x12\x03\x03\x04\x16\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\x11\n\x0c\n\x05\
     \x04\0\x02\0\x03\x12\x03\x03\x14\x15\n\x0b\n\x04\x04\0\x02\x01\x12\x03\

+ 1 - 1
rust-lib/flowy-dart-notify/src/protobuf/proto/subject.proto

@@ -1,6 +1,6 @@
 syntax = "proto3";
 
-message ObservableSubject {
+message SubscribeObject {
     string source = 1;
     int32 ty = 2;
     string id = 3;

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

@@ -69,7 +69,7 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         | "DocError"
         | "FFIRequest"
         | "FFIResponse"
-        | "ObservableSubject"
+        | "SubscribeObject"
         | "SignInRequest"
         | "SignInParams"
         | "SignInResponse"
@@ -85,14 +85,14 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         "ViewType"
         | "WorkspaceEvent"
         | "ErrorCode"
-        | "Notification"
+        | "WorkspaceNotification"
         | "WsModule"
         | "RevType"
         | "WsDataType"
         | "DocObservable"
         | "FFIStatusCode"
         | "UserEvent"
-        | "UserObservable"
+        | "UserNotification"
         => TypeCategory::Enum,
 
         "Option" => TypeCategory::Opt,

+ 5 - 5
rust-lib/flowy-user/src/notify/observable.rs

@@ -5,21 +5,21 @@ use flowy_dart_notify::DartNotifyBuilder;
 const OBSERVABLE_CATEGORY: &'static str = "User";
 
 #[derive(ProtoBuf_Enum, Debug)]
-pub(crate) enum UserObservable {
+pub(crate) enum UserNotification {
     Unknown            = 0,
     UserAuthChanged    = 1,
     UserProfileUpdated = 2,
     UserUnauthorized   = 3,
 }
 
-impl std::default::Default for UserObservable {
-    fn default() -> Self { UserObservable::Unknown }
+impl std::default::Default for UserNotification {
+    fn default() -> Self { UserNotification::Unknown }
 }
 
-impl std::convert::Into<i32> for UserObservable {
+impl std::convert::Into<i32> for UserNotification {
     fn into(self) -> i32 { self as i32 }
 }
 
-pub(crate) fn dart_notify(id: &str, ty: UserObservable) -> DartNotifyBuilder {
+pub(crate) fn dart_notify(id: &str, ty: UserNotification) -> DartNotifyBuilder {
     DartNotifyBuilder::new(id, ty, OBSERVABLE_CATEGORY)
 }

+ 20 - 20
rust-lib/flowy-user/src/protobuf/model/observable.rs

@@ -24,34 +24,34 @@
 // const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
 
 #[derive(Clone,PartialEq,Eq,Debug,Hash)]
-pub enum UserObservable {
+pub enum UserNotification {
     Unknown = 0,
     UserAuthChanged = 1,
     UserProfileUpdated = 2,
     UserUnauthorized = 3,
 }
 
-impl ::protobuf::ProtobufEnum for UserObservable {
+impl ::protobuf::ProtobufEnum for UserNotification {
     fn value(&self) -> i32 {
         *self as i32
     }
 
-    fn from_i32(value: i32) -> ::std::option::Option<UserObservable> {
+    fn from_i32(value: i32) -> ::std::option::Option<UserNotification> {
         match value {
-            0 => ::std::option::Option::Some(UserObservable::Unknown),
-            1 => ::std::option::Option::Some(UserObservable::UserAuthChanged),
-            2 => ::std::option::Option::Some(UserObservable::UserProfileUpdated),
-            3 => ::std::option::Option::Some(UserObservable::UserUnauthorized),
+            0 => ::std::option::Option::Some(UserNotification::Unknown),
+            1 => ::std::option::Option::Some(UserNotification::UserAuthChanged),
+            2 => ::std::option::Option::Some(UserNotification::UserProfileUpdated),
+            3 => ::std::option::Option::Some(UserNotification::UserUnauthorized),
             _ => ::std::option::Option::None
         }
     }
 
     fn values() -> &'static [Self] {
-        static values: &'static [UserObservable] = &[
-            UserObservable::Unknown,
-            UserObservable::UserAuthChanged,
-            UserObservable::UserProfileUpdated,
-            UserObservable::UserUnauthorized,
+        static values: &'static [UserNotification] = &[
+            UserNotification::Unknown,
+            UserNotification::UserAuthChanged,
+            UserNotification::UserProfileUpdated,
+            UserNotification::UserUnauthorized,
         ];
         values
     }
@@ -59,32 +59,32 @@ impl ::protobuf::ProtobufEnum for UserObservable {
     fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
         static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
         descriptor.get(|| {
-            ::protobuf::reflect::EnumDescriptor::new_pb_name::<UserObservable>("UserObservable", file_descriptor_proto())
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<UserNotification>("UserNotification", file_descriptor_proto())
         })
     }
 }
 
-impl ::std::marker::Copy for UserObservable {
+impl ::std::marker::Copy for UserNotification {
 }
 
-impl ::std::default::Default for UserObservable {
+impl ::std::default::Default for UserNotification {
     fn default() -> Self {
-        UserObservable::Unknown
+        UserNotification::Unknown
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for UserObservable {
+impl ::protobuf::reflect::ProtobufValue for UserNotification {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
     }
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x10observable.proto*`\n\x0eUserObservable\x12\x0b\n\x07Unknown\x10\0\
-    \x12\x13\n\x0fUserAuthChanged\x10\x01\x12\x16\n\x12UserProfileUpdated\
+    \n\x10observable.proto*b\n\x10UserNotification\x12\x0b\n\x07Unknown\x10\
+    \0\x12\x13\n\x0fUserAuthChanged\x10\x01\x12\x16\n\x12UserProfileUpdated\
     \x10\x02\x12\x14\n\x10UserUnauthorized\x10\x03J\xce\x01\n\x06\x12\x04\0\
     \0\x07\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\
-    \x07\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x13\n\x0b\n\x04\x05\0\x02\0\
+    \x07\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x15\n\x0b\n\x04\x05\0\x02\0\
     \x12\x03\x03\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x0b\n\
     \x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\
     \x12\x03\x04\x04\x18\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x13\n\

+ 1 - 1
rust-lib/flowy-user/src/protobuf/proto/observable.proto

@@ -1,6 +1,6 @@
 syntax = "proto3";
 
-enum UserObservable {
+enum UserNotification {
     Unknown = 0;
     UserAuthChanged = 1;
     UserProfileUpdated = 2;

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

@@ -70,7 +70,9 @@ impl ResponseMiddleware for Middleware {
                     None => {},
                     Some(token) => {
                         let error = UserError::new(ErrorCode::UserUnauthorized, "");
-                        dart_notify(token, UserObservable::UserUnauthorized).error(error).send()
+                        dart_notify(token, UserNotification::UserUnauthorized)
+                            .error(error)
+                            .send()
                     },
                 }
             }

+ 9 - 9
rust-lib/flowy-user/src/services/user/user_session.rs

@@ -194,12 +194,14 @@ impl UserSession {
         tokio::spawn(async move {
             match server.get_user(&token).await {
                 Ok(profile) => {
-                    dart_notify(&token, UserObservable::UserProfileUpdated)
+                    dart_notify(&token, UserNotification::UserProfileUpdated)
                         .payload(profile)
                         .send();
                 },
                 Err(e) => {
-                    dart_notify(&token, UserObservable::UserProfileUpdated).error(e).send();
+                    dart_notify(&token, UserNotification::UserProfileUpdated)
+                        .error(e)
+                        .send();
                 },
             }
         });
@@ -299,13 +301,11 @@ impl UserSession {
                         match state {
                             WsState::Init => {},
                             WsState::Connected(_) => {},
-                            WsState::Disconnected(_) => {
-                                match ws_controller.retry().await {
-                                    Ok(_) => {},
-                                    Err(e) => {
-                                        log::error!("Retry websocket connect failed: {:?}", e);
-                                    }
-                                }
+                            WsState::Disconnected(_) => match ws_controller.retry().await {
+                                Ok(_) => {},
+                                Err(e) => {
+                                    log::error!("Retry websocket connect failed: {:?}", e);
+                                },
                             },
                         }
                     },

+ 12 - 7
rust-lib/flowy-workspace/src/notify/observable.rs

@@ -3,7 +3,7 @@ use flowy_derive::ProtoBuf_Enum;
 const OBSERVABLE_CATEGORY: &'static str = "Workspace";
 
 #[derive(ProtoBuf_Enum, Debug)]
-pub(crate) enum Notification {
+pub(crate) enum WorkspaceNotification {
     Unknown              = 0,
     UserCreateWorkspace  = 10,
     UserDeleteWorkspace  = 11,
@@ -15,18 +15,23 @@ pub(crate) enum Notification {
     AppViewsChanged      = 24,
     ViewUpdated          = 31,
     UserUnauthorized     = 100,
+    TrashUpdated         = 1000,
 }
 
-impl std::default::Default for Notification {
-    fn default() -> Self { Notification::Unknown }
+impl std::default::Default for WorkspaceNotification {
+    fn default() -> Self { WorkspaceNotification::Unknown }
 }
 
-impl std::convert::Into<i32> for Notification {
+impl std::convert::Into<i32> for WorkspaceNotification {
     fn into(self) -> i32 { self as i32 }
 }
 
-pub(crate) fn dart_notify(id: &str, ty: Notification) -> DartNotifyBuilder {
-    tracing::Span::current().record("dart_notify", &format!("{:?} => notify_id: {}", ty, id).as_str());
-
+#[tracing::instrument(level = "debug")]
+pub(crate) fn send_dart_notification(id: &str, ty: WorkspaceNotification) -> DartNotifyBuilder {
     DartNotifyBuilder::new(id, ty, OBSERVABLE_CATEGORY)
 }
+
+#[tracing::instrument(level = "debug")]
+pub(crate) fn send_anonymous_dart_notification(ty: WorkspaceNotification) -> DartNotifyBuilder {
+    DartNotifyBuilder::new("", ty, OBSERVABLE_CATEGORY)
+}

+ 68 - 62
rust-lib/flowy-workspace/src/protobuf/model/observable.rs

@@ -24,7 +24,7 @@
 // const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
 
 #[derive(Clone,PartialEq,Eq,Debug,Hash)]
-pub enum Notification {
+pub enum WorkspaceNotification {
     Unknown = 0,
     UserCreateWorkspace = 10,
     UserDeleteWorkspace = 11,
@@ -36,43 +36,46 @@ pub enum Notification {
     AppViewsChanged = 24,
     ViewUpdated = 31,
     UserUnauthorized = 100,
+    TrashUpdated = 1000,
 }
 
-impl ::protobuf::ProtobufEnum for Notification {
+impl ::protobuf::ProtobufEnum for WorkspaceNotification {
     fn value(&self) -> i32 {
         *self as i32
     }
 
-    fn from_i32(value: i32) -> ::std::option::Option<Notification> {
+    fn from_i32(value: i32) -> ::std::option::Option<WorkspaceNotification> {
         match value {
-            0 => ::std::option::Option::Some(Notification::Unknown),
-            10 => ::std::option::Option::Some(Notification::UserCreateWorkspace),
-            11 => ::std::option::Option::Some(Notification::UserDeleteWorkspace),
-            12 => ::std::option::Option::Some(Notification::WorkspaceUpdated),
-            13 => ::std::option::Option::Some(Notification::WorkspaceCreateApp),
-            14 => ::std::option::Option::Some(Notification::WorkspaceDeleteApp),
-            15 => ::std::option::Option::Some(Notification::WorkspaceListUpdated),
-            21 => ::std::option::Option::Some(Notification::AppUpdated),
-            24 => ::std::option::Option::Some(Notification::AppViewsChanged),
-            31 => ::std::option::Option::Some(Notification::ViewUpdated),
-            100 => ::std::option::Option::Some(Notification::UserUnauthorized),
+            0 => ::std::option::Option::Some(WorkspaceNotification::Unknown),
+            10 => ::std::option::Option::Some(WorkspaceNotification::UserCreateWorkspace),
+            11 => ::std::option::Option::Some(WorkspaceNotification::UserDeleteWorkspace),
+            12 => ::std::option::Option::Some(WorkspaceNotification::WorkspaceUpdated),
+            13 => ::std::option::Option::Some(WorkspaceNotification::WorkspaceCreateApp),
+            14 => ::std::option::Option::Some(WorkspaceNotification::WorkspaceDeleteApp),
+            15 => ::std::option::Option::Some(WorkspaceNotification::WorkspaceListUpdated),
+            21 => ::std::option::Option::Some(WorkspaceNotification::AppUpdated),
+            24 => ::std::option::Option::Some(WorkspaceNotification::AppViewsChanged),
+            31 => ::std::option::Option::Some(WorkspaceNotification::ViewUpdated),
+            100 => ::std::option::Option::Some(WorkspaceNotification::UserUnauthorized),
+            1000 => ::std::option::Option::Some(WorkspaceNotification::TrashUpdated),
             _ => ::std::option::Option::None
         }
     }
 
     fn values() -> &'static [Self] {
-        static values: &'static [Notification] = &[
-            Notification::Unknown,
-            Notification::UserCreateWorkspace,
-            Notification::UserDeleteWorkspace,
-            Notification::WorkspaceUpdated,
-            Notification::WorkspaceCreateApp,
-            Notification::WorkspaceDeleteApp,
-            Notification::WorkspaceListUpdated,
-            Notification::AppUpdated,
-            Notification::AppViewsChanged,
-            Notification::ViewUpdated,
-            Notification::UserUnauthorized,
+        static values: &'static [WorkspaceNotification] = &[
+            WorkspaceNotification::Unknown,
+            WorkspaceNotification::UserCreateWorkspace,
+            WorkspaceNotification::UserDeleteWorkspace,
+            WorkspaceNotification::WorkspaceUpdated,
+            WorkspaceNotification::WorkspaceCreateApp,
+            WorkspaceNotification::WorkspaceDeleteApp,
+            WorkspaceNotification::WorkspaceListUpdated,
+            WorkspaceNotification::AppUpdated,
+            WorkspaceNotification::AppViewsChanged,
+            WorkspaceNotification::ViewUpdated,
+            WorkspaceNotification::UserUnauthorized,
+            WorkspaceNotification::TrashUpdated,
         ];
         values
     }
@@ -80,58 +83,61 @@ impl ::protobuf::ProtobufEnum for Notification {
     fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
         static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
         descriptor.get(|| {
-            ::protobuf::reflect::EnumDescriptor::new_pb_name::<Notification>("Notification", file_descriptor_proto())
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<WorkspaceNotification>("WorkspaceNotification", file_descriptor_proto())
         })
     }
 }
 
-impl ::std::marker::Copy for Notification {
+impl ::std::marker::Copy for WorkspaceNotification {
 }
 
-impl ::std::default::Default for Notification {
+impl ::std::default::Default for WorkspaceNotification {
     fn default() -> Self {
-        Notification::Unknown
+        WorkspaceNotification::Unknown
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for Notification {
+impl ::protobuf::reflect::ProtobufValue for WorkspaceNotification {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
     }
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x10observable.proto*\xf9\x01\n\x0cNotification\x12\x0b\n\x07Unknown\
-    \x10\0\x12\x17\n\x13UserCreateWorkspace\x10\n\x12\x17\n\x13UserDeleteWor\
-    kspace\x10\x0b\x12\x14\n\x10WorkspaceUpdated\x10\x0c\x12\x16\n\x12Worksp\
-    aceCreateApp\x10\r\x12\x16\n\x12WorkspaceDeleteApp\x10\x0e\x12\x18\n\x14\
-    WorkspaceListUpdated\x10\x0f\x12\x0e\n\nAppUpdated\x10\x15\x12\x13\n\x0f\
-    AppViewsChanged\x10\x18\x12\x0f\n\x0bViewUpdated\x10\x1f\x12\x14\n\x10Us\
-    erUnauthorized\x10dJ\xed\x03\n\x06\x12\x04\0\0\x0e\x01\n\x08\n\x01\x0c\
-    \x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x0e\x01\n\n\n\x03\x05\0\
-    \x01\x12\x03\x02\x05\x11\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x10\n\
-    \x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x0b\n\x0c\n\x05\x05\0\x02\0\
-    \x02\x12\x03\x03\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x1d\n\
-    \x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x17\n\x0c\n\x05\x05\0\x02\
-    \x01\x02\x12\x03\x04\x1a\x1c\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\
-    \x1d\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x17\n\x0c\n\x05\x05\0\
-    \x02\x02\x02\x12\x03\x05\x1a\x1c\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\
-    \x04\x1a\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x14\n\x0c\n\x05\
-    \x05\0\x02\x03\x02\x12\x03\x06\x17\x19\n\x0b\n\x04\x05\0\x02\x04\x12\x03\
-    \x07\x04\x1c\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x16\n\x0c\n\
-    \x05\x05\0\x02\x04\x02\x12\x03\x07\x19\x1b\n\x0b\n\x04\x05\0\x02\x05\x12\
-    \x03\x08\x04\x1c\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x16\n\x0c\
-    \n\x05\x05\0\x02\x05\x02\x12\x03\x08\x19\x1b\n\x0b\n\x04\x05\0\x02\x06\
-    \x12\x03\t\x04\x1e\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\t\x04\x18\n\x0c\
-    \n\x05\x05\0\x02\x06\x02\x12\x03\t\x1b\x1d\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\x0e\n\x0c\n\
-    \x05\x05\0\x02\x07\x02\x12\x03\n\x11\x13\n\x0b\n\x04\x05\0\x02\x08\x12\
-    \x03\x0b\x04\x19\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\x13\n\x0c\
-    \n\x05\x05\0\x02\x08\x02\x12\x03\x0b\x16\x18\n\x0b\n\x04\x05\0\x02\t\x12\
-    \x03\x0c\x04\x15\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x0c\x04\x0f\n\x0c\n\
-    \x05\x05\0\x02\t\x02\x12\x03\x0c\x12\x14\n\x0b\n\x04\x05\0\x02\n\x12\x03\
-    \r\x04\x1b\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\r\x04\x14\n\x0c\n\x05\x05\
-    \0\x02\n\x02\x12\x03\r\x17\x1ab\x06proto3\
+    \n\x10observable.proto*\x95\x02\n\x15WorkspaceNotification\x12\x0b\n\x07\
+    Unknown\x10\0\x12\x17\n\x13UserCreateWorkspace\x10\n\x12\x17\n\x13UserDe\
+    leteWorkspace\x10\x0b\x12\x14\n\x10WorkspaceUpdated\x10\x0c\x12\x16\n\
+    \x12WorkspaceCreateApp\x10\r\x12\x16\n\x12WorkspaceDeleteApp\x10\x0e\x12\
+    \x18\n\x14WorkspaceListUpdated\x10\x0f\x12\x0e\n\nAppUpdated\x10\x15\x12\
+    \x13\n\x0fAppViewsChanged\x10\x18\x12\x0f\n\x0bViewUpdated\x10\x1f\x12\
+    \x14\n\x10UserUnauthorized\x10d\x12\x11\n\x0cTrashUpdated\x10\xe8\x07J\
+    \x96\x04\n\x06\x12\x04\0\0\x0f\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\
+    \x02\x05\0\x12\x04\x02\0\x0f\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x1a\
+    \n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\
+    \x12\x03\x03\x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\x0e\x0f\n\
+    \x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x1d\n\x0c\n\x05\x05\0\x02\x01\
+    \x01\x12\x03\x04\x04\x17\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x04\x1a\
+    \x1c\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\x1d\n\x0c\n\x05\x05\0\x02\
+    \x02\x01\x12\x03\x05\x04\x17\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x05\
+    \x1a\x1c\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\x04\x1a\n\x0c\n\x05\x05\0\
+    \x02\x03\x01\x12\x03\x06\x04\x14\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\
+    \x06\x17\x19\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x07\x04\x1c\n\x0c\n\x05\
+    \x05\0\x02\x04\x01\x12\x03\x07\x04\x16\n\x0c\n\x05\x05\0\x02\x04\x02\x12\
+    \x03\x07\x19\x1b\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x08\x04\x1c\n\x0c\n\
+    \x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x16\n\x0c\n\x05\x05\0\x02\x05\x02\
+    \x12\x03\x08\x19\x1b\n\x0b\n\x04\x05\0\x02\x06\x12\x03\t\x04\x1e\n\x0c\n\
+    \x05\x05\0\x02\x06\x01\x12\x03\t\x04\x18\n\x0c\n\x05\x05\0\x02\x06\x02\
+    \x12\x03\t\x1b\x1d\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\x0e\n\x0c\n\x05\x05\0\x02\x07\x02\
+    \x12\x03\n\x11\x13\n\x0b\n\x04\x05\0\x02\x08\x12\x03\x0b\x04\x19\n\x0c\n\
+    \x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\x13\n\x0c\n\x05\x05\0\x02\x08\x02\
+    \x12\x03\x0b\x16\x18\n\x0b\n\x04\x05\0\x02\t\x12\x03\x0c\x04\x15\n\x0c\n\
+    \x05\x05\0\x02\t\x01\x12\x03\x0c\x04\x0f\n\x0c\n\x05\x05\0\x02\t\x02\x12\
+    \x03\x0c\x12\x14\n\x0b\n\x04\x05\0\x02\n\x12\x03\r\x04\x1b\n\x0c\n\x05\
+    \x05\0\x02\n\x01\x12\x03\r\x04\x14\n\x0c\n\x05\x05\0\x02\n\x02\x12\x03\r\
+    \x17\x1a\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x0e\x04\x18\n\x0c\n\x05\x05\0\
+    \x02\x0b\x01\x12\x03\x0e\x04\x10\n\x0c\n\x05\x05\0\x02\x0b\x02\x12\x03\
+    \x0e\x13\x17b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -1,6 +1,6 @@
 syntax = "proto3";
 
-enum Notification {
+enum WorkspaceNotification {
     Unknown = 0;
     UserCreateWorkspace = 10;
     UserDeleteWorkspace = 11;
@@ -12,4 +12,5 @@ enum Notification {
     AppViewsChanged = 24;
     ViewUpdated = 31;
     UserUnauthorized = 100;
+    TrashUpdated = 1000;
 }

+ 5 - 3
rust-lib/flowy-workspace/src/services/app_controller.rs

@@ -36,7 +36,7 @@ impl AppController {
         conn.immediate_transaction::<_, WorkspaceError, _>(|| {
             let _ = self.save_app(app.clone(), &*conn)?;
             let apps = self.read_local_apps(&app.workspace_id, &*conn)?;
-            dart_notify(&app.workspace_id, Notification::WorkspaceCreateApp)
+            send_dart_notification(&app.workspace_id, WorkspaceNotification::WorkspaceCreateApp)
                 .payload(apps)
                 .send();
             Ok(())
@@ -65,7 +65,7 @@ impl AppController {
         conn.immediate_transaction::<_, WorkspaceError, _>(|| {
             let app = self.sql.delete_app(app_id, &*conn)?;
             let apps = self.read_local_apps(&app.workspace_id, &*conn)?;
-            dart_notify(&app.workspace_id, Notification::WorkspaceDeleteApp)
+            send_dart_notification(&app.workspace_id, WorkspaceNotification::WorkspaceDeleteApp)
                 .payload(apps)
                 .send();
             Ok(())
@@ -88,7 +88,9 @@ impl AppController {
         conn.immediate_transaction::<_, WorkspaceError, _>(|| {
             let _ = self.sql.update_app(changeset, conn)?;
             let app: App = self.sql.read_app(&app_id, None, conn)?.into();
-            dart_notify(&app_id, Notification::AppUpdated).payload(app).send();
+            send_dart_notification(&app_id, WorkspaceNotification::AppUpdated)
+                .payload(app)
+                .send();
             Ok(())
         })?;
 

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

@@ -21,7 +21,9 @@ impl ResponseMiddleware for WorkspaceMiddleware {
                     None => {},
                     Some(token) => {
                         let error = WorkspaceError::new(ErrorCode::UserUnauthorized, "");
-                        dart_notify(token, Notification::UserUnauthorized).error(error).send()
+                        send_dart_notification(token, WorkspaceNotification::UserUnauthorized)
+                            .error(error)
+                            .send()
                     },
                 }
             }

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

@@ -2,6 +2,7 @@ use crate::{
     entities::trash::{RepeatedTrash, Trash},
     errors::{WorkspaceError, WorkspaceResult},
     module::WorkspaceDatabase,
+    notify::{send_anonymous_dart_notification, WorkspaceNotification},
     sql_tables::trash::{TrashSource, TrashTable, TrashTableSql},
 };
 use flowy_database::SqliteConnection;
@@ -60,16 +61,20 @@ impl TrashCan {
     #[tracing::instrument(level = "debug", skip(self), fields(putback)  err)]
     pub fn putback(&self, trash_id: &str) -> WorkspaceResult<()> {
         let conn = self.database.db_connection()?;
-        // Opti: transaction
-        let trash_table = TrashTableSql::read(trash_id, &*conn)?;
-        let _ = TrashTableSql::delete_trash(trash_id, &*conn)?;
-        tracing::Span::current().record(
-            "putback",
-            &format!("{:?}: {}", &trash_table.source, trash_table.id).as_str(),
-        );
+        let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
+            let trash_table = TrashTableSql::read(trash_id, &*conn)?;
+            let _ = TrashTableSql::delete_trash(trash_id, &*conn)?;
+            tracing::Span::current().record(
+                "putback",
+                &format!("{:?}: {}", &trash_table.source, trash_table.id).as_str(),
+            );
+
+            self.notify
+                .send(TrashEvent::Putback(trash_table.source, trash_table.id));
 
-        self.notify
-            .send(TrashEvent::Putback(trash_table.source, trash_table.id));
+            let _ = self.notify_dart_trash_did_update(&conn)?;
+            Ok(())
+        })?;
 
         Ok(())
     }
@@ -80,7 +85,7 @@ impl TrashCan {
         let trash_table = TrashTableSql::read(trash_id, &*conn)?;
         let _ = TrashTableSql::delete_trash(trash_id, &*conn)?;
 
-        self.notify.send(TrashEvent::Delete(trash_table.source, trash_table.id));
+        let _ = self.notify.send(TrashEvent::Delete(trash_table.source, trash_table.id));
         Ok(())
     }
 
@@ -108,8 +113,19 @@ impl TrashCan {
             &format!("{:?}: {}", &trash_table.source, trash_table.id).as_str(),
         );
 
-        let trash_id = trash_table.id.clone();
         let _ = TrashTableSql::create_trash(trash_table, &*conn)?;
+        let _ = self.notify_dart_trash_did_update(&conn)?;
+
+        Ok(())
+    }
+
+    fn notify_dart_trash_did_update(&self, conn: &SqliteConnection) -> WorkspaceResult<()> {
+        // Opti: only push the changeset
+        let repeated_trash = TrashTableSql::read_all(conn)?;
+        send_anonymous_dart_notification(WorkspaceNotification::TrashUpdated)
+            .payload(repeated_trash)
+            .send();
+
         Ok(())
     }
 }

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

@@ -2,7 +2,7 @@ use crate::{
     entities::view::{CreateViewParams, UpdateViewParams, View},
     errors::WorkspaceError,
     module::WorkspaceDatabase,
-    notify::dart_notify,
+    notify::send_dart_notification,
     services::{helper::spawn, server::Server},
     sql_tables::view::{ViewTable, ViewTableChangeset, ViewTableSql},
 };
@@ -14,7 +14,7 @@ use crate::{
     },
     errors::internal_error,
     module::WorkspaceUser,
-    notify::Notification,
+    notify::WorkspaceNotification,
     services::{TrashCan, TrashEvent},
     sql_tables::trash::TrashSource,
 };
@@ -70,7 +70,7 @@ impl ViewController {
             self.document.create(CreateDocParams::new(&view.id, params.data))?;
 
             let repeated_view = ViewTableSql::read_views(&view.belong_to_id, conn)?;
-            dart_notify(&view.belong_to_id, Notification::AppViewsChanged)
+            send_dart_notification(&view.belong_to_id, WorkspaceNotification::AppViewsChanged)
                 .payload(repeated_view)
                 .send();
             Ok(())
@@ -110,7 +110,7 @@ impl ViewController {
 
             let repeated_view = ViewTableSql::read_views(&view_table.belong_to_id, conn)?;
 
-            dart_notify(&view_table.belong_to_id, Notification::AppViewsChanged)
+            send_dart_notification(&view_table.belong_to_id, WorkspaceNotification::AppViewsChanged)
                 .payload(repeated_view)
                 .send();
             Ok(())
@@ -139,7 +139,7 @@ impl ViewController {
             let view: View = ViewTableSql::read_view(&view_id, conn)?.into();
             match params.is_trash {
                 None => {
-                    dart_notify(&view_id, Notification::ViewUpdated)
+                    send_dart_notification(&view_id, WorkspaceNotification::ViewUpdated)
                         .payload(view.clone())
                         .send();
                 },
@@ -245,7 +245,7 @@ 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)?;
-    dart_notify(belong_to_id, Notification::AppViewsChanged)
+    send_dart_notification(belong_to_id, WorkspaceNotification::AppViewsChanged)
         .payload(repeated_view)
         .send();
     Ok(())

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

@@ -69,7 +69,7 @@ impl WorkspaceController {
         conn.immediate_transaction::<_, WorkspaceError, _>(|| {
             self.workspace_sql.create_workspace(workspace_table, conn)?;
             let repeated_workspace = self.read_local_workspaces(None, &user_id, conn)?;
-            dart_notify(&token, Notification::UserCreateWorkspace)
+            send_dart_notification(&token, WorkspaceNotification::UserCreateWorkspace)
                 .payload(repeated_workspace)
                 .send();
 
@@ -88,7 +88,7 @@ impl WorkspaceController {
             let _ = self.workspace_sql.update_workspace(changeset, conn)?;
             let user_id = self.user.user_id()?;
             let workspace = self.read_local_workspace(workspace_id.clone(), &user_id, conn)?;
-            dart_notify(&workspace_id, Notification::WorkspaceUpdated)
+            send_dart_notification(&workspace_id, WorkspaceNotification::WorkspaceUpdated)
                 .payload(workspace)
                 .send();
 
@@ -108,7 +108,7 @@ impl WorkspaceController {
         conn.immediate_transaction::<_, WorkspaceError, _>(|| {
             let _ = self.workspace_sql.delete_workspace(workspace_id, conn)?;
             let repeated_workspace = self.read_local_workspaces(None, &user_id, conn)?;
-            dart_notify(&token, Notification::UserDeleteWorkspace)
+            send_dart_notification(&token, WorkspaceNotification::UserDeleteWorkspace)
                 .payload(repeated_workspace)
                 .send();
 
@@ -297,7 +297,7 @@ impl WorkspaceController {
                 Ok(())
             })?;
 
-            dart_notify(&token, Notification::WorkspaceListUpdated)
+            send_dart_notification(&token, WorkspaceNotification::WorkspaceListUpdated)
                 .payload(workspaces)
                 .send();
             Result::<(), WorkspaceError>::Ok(())