diff --git a/frontend/app_flowy/lib/plugins/trash/application/trash_bloc.dart b/frontend/app_flowy/lib/plugins/trash/application/trash_bloc.dart index 0286747fd5..7e96fdc601 100644 --- a/frontend/app_flowy/lib/plugins/trash/application/trash_bloc.dart +++ b/frontend/app_flowy/lib/plugins/trash/application/trash_bloc.dart @@ -10,14 +10,16 @@ import 'package:app_flowy/plugins/trash/application/trash_listener.dart'; part 'trash_bloc.freezed.dart'; class TrashBloc extends Bloc { - final TrashService service; - final TrashListener listener; - TrashBloc({required this.service, required this.listener}) - : super(TrashState.init()) { + final TrashService _service; + final TrashListener _listener; + TrashBloc() + : _service = TrashService(), + _listener = TrashListener(), + super(TrashState.init()) { on((event, emit) async { await event.map(initial: (e) async { - listener.start(trashUpdated: _listenTrashUpdated); - final result = await service.readTrash(); + _listener.start(trashUpdated: _listenTrashUpdated); + final result = await _service.readTrash(); emit(result.fold( (object) => state.copyWith( objects: object.items, successOrFailure: left(unit)), @@ -26,17 +28,17 @@ class TrashBloc extends Bloc { }, didReceiveTrash: (e) async { emit(state.copyWith(objects: e.trash)); }, putback: (e) async { - final result = await service.putback(e.trashId); + final result = await _service.putback(e.trashId); await _handleResult(result, emit); }, delete: (e) async { final result = - await service.deleteViews([Tuple2(e.trash.id, e.trash.ty)]); + await _service.deleteViews([Tuple2(e.trash.id, e.trash.ty)]); await _handleResult(result, emit); }, deleteAll: (e) async { - final result = await service.deleteAll(); + final result = await _service.deleteAll(); await _handleResult(result, emit); }, restoreAll: (e) async { - final result = await service.restoreAll(); + final result = await _service.restoreAll(); await _handleResult(result, emit); }); }); @@ -63,7 +65,7 @@ class TrashBloc extends Bloc { @override Future close() async { - await listener.close(); + await _listener.close(); return super.close(); } } diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index 864af3e0bc..b2b10cbf07 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -92,14 +92,6 @@ void _resolveFolderDeps(GetIt getIt) { ), ); - //Menu - getIt.registerFactoryParam( - (user, workspaceId) => MenuBloc( - workspaceId: workspaceId, - listener: getIt(param1: user, param2: workspaceId), - ), - ); - getIt.registerFactoryParam( (user, _) => MenuUserBloc(user), ); @@ -123,10 +115,7 @@ void _resolveFolderDeps(GetIt getIt) { getIt.registerLazySingleton(() => TrashService()); getIt.registerLazySingleton(() => TrashListener()); getIt.registerFactory( - () => TrashBloc( - service: getIt(), - listener: getIt(), - ), + () => TrashBloc(), ); } diff --git a/frontend/app_flowy/lib/startup/startup.dart b/frontend/app_flowy/lib/startup/startup.dart index f71df96f23..77d0cc3d33 100644 --- a/frontend/app_flowy/lib/startup/startup.dart +++ b/frontend/app_flowy/lib/startup/startup.dart @@ -39,9 +39,9 @@ class FlowyRunner { // add task getIt().addTask(InitRustSDKTask()); + getIt().addTask(PluginLoadTask()); if (!env.isTest()) { - getIt().addTask(PluginLoadTask()); getIt().addTask(InitAppWidgetTask()); getIt().addTask(InitPlatformServiceTask()); } diff --git a/frontend/app_flowy/lib/workspace/application/app/app_bloc.dart b/frontend/app_flowy/lib/workspace/application/app/app_bloc.dart index 654d267796..187a7155ad 100644 --- a/frontend/app_flowy/lib/workspace/application/app/app_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/app/app_bloc.dart @@ -18,11 +18,10 @@ import 'package:dartz/dartz.dart'; part 'app_bloc.freezed.dart'; class AppBloc extends Bloc { - final AppPB app; final AppService appService; final AppListener appListener; - AppBloc({required this.app}) + AppBloc({required AppPB app}) : appService = AppService(), appListener = AppListener(appId: app.id), super(AppState.initial(app)) { @@ -34,8 +33,6 @@ class AppBloc extends Bloc { await _createView(value, emit); }, loadViews: (_) async { await _loadViews(emit); - }, didReceiveViewUpdated: (e) async { - await _didReceiveViewUpdated(e.views, emit); }, delete: (e) async { await _deleteApp(emit); }, deleteView: (deletedView) async { @@ -43,23 +40,26 @@ class AppBloc extends Bloc { }, rename: (e) async { await _renameView(e, emit); }, appDidUpdate: (e) async { - emit(state.copyWith(app: e.app)); + final latestCreatedView = state.latestCreatedView; + final views = e.app.belongings.items; + AppState newState = state.copyWith( + views: views, + app: e.app, + ); + if (latestCreatedView != null) { + final index = + views.indexWhere((element) => element.id == latestCreatedView.id); + if (index == -1) { + newState = newState.copyWith(latestCreatedView: null); + } + } + emit(newState); }); }); } void _startListening() { appListener.start( - onViewsChanged: (result) { - result.fold( - (views) { - if (!isClosed) { - add(AppEvent.didReceiveViewUpdated(views)); - } - }, - (error) => Log.error(error), - ); - }, onAppUpdated: (app) { if (!isClosed) { add(AppEvent.appDidUpdate(app)); @@ -69,7 +69,8 @@ class AppBloc extends Bloc { } Future _renameView(Rename e, Emitter emit) async { - final result = await appService.updateApp(appId: app.id, name: e.newName); + final result = + await appService.updateApp(appId: state.app.id, name: e.newName); result.fold( (l) => emit(state.copyWith(successOrFailure: left(unit))), (error) => emit(state.copyWith(successOrFailure: right(error))), @@ -78,7 +79,7 @@ class AppBloc extends Bloc { // Delete the current app Future _deleteApp(Emitter emit) async { - final result = await appService.delete(appId: app.id); + final result = await appService.delete(appId: state.app.id); result.fold( (unit) => emit(state.copyWith(successOrFailure: left(unit))), (error) => emit(state.copyWith(successOrFailure: right(error))), @@ -95,7 +96,7 @@ class AppBloc extends Bloc { Future _createView(CreateView value, Emitter emit) async { final result = await appService.createView( - appId: app.id, + appId: state.app.id, name: value.name, desc: value.desc ?? "", dataFormatType: value.pluginBuilder.dataFormatType, @@ -120,25 +121,8 @@ class AppBloc extends Bloc { return super.close(); } - Future _didReceiveViewUpdated( - List views, - Emitter emit, - ) async { - final latestCreatedView = state.latestCreatedView; - AppState newState = state.copyWith(views: views); - if (latestCreatedView != null) { - final index = - views.indexWhere((element) => element.id == latestCreatedView.id); - if (index == -1) { - newState = newState.copyWith(latestCreatedView: null); - } - } - - emit(newState); - } - Future _loadViews(Emitter emit) async { - final viewsOrFailed = await appService.getViews(appId: app.id); + final viewsOrFailed = await appService.getViews(appId: state.app.id); viewsOrFailed.fold( (views) => emit(state.copyWith(views: views)), (error) { @@ -161,8 +145,6 @@ class AppEvent with _$AppEvent { const factory AppEvent.delete() = DeleteApp; const factory AppEvent.deleteView(String viewId) = DeleteView; const factory AppEvent.rename(String newName) = Rename; - const factory AppEvent.didReceiveViewUpdated(List views) = - ReceiveViews; const factory AppEvent.appDidUpdate(AppPB app) = AppDidUpdate; } diff --git a/frontend/app_flowy/lib/workspace/application/app/app_listener.dart b/frontend/app_flowy/lib/workspace/application/app/app_listener.dart index 6edf1a4df2..7019200261 100644 --- a/frontend/app_flowy/lib/workspace/application/app/app_listener.dart +++ b/frontend/app_flowy/lib/workspace/application/app/app_listener.dart @@ -11,11 +11,11 @@ import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart'; import 'package:flowy_sdk/rust_stream.dart'; typedef AppDidUpdateCallback = void Function(AppPB app); -typedef ViewsDidChangeCallback = void Function(Either, FlowyError> viewsOrFailed); +typedef ViewsDidChangeCallback = void Function( + Either, FlowyError> viewsOrFailed); class AppListener { StreamSubscription? _subscription; - ViewsDidChangeCallback? _viewsChanged; AppDidUpdateCallback? _updated; FolderNotificationParser? _parser; String appId; @@ -24,26 +24,16 @@ class AppListener { required this.appId, }); - void start({ViewsDidChangeCallback? onViewsChanged, AppDidUpdateCallback? onAppUpdated}) { - _viewsChanged = onViewsChanged; + void start({AppDidUpdateCallback? onAppUpdated}) { _updated = onAppUpdated; - _parser = FolderNotificationParser(id: appId, callback: _bservableCallback); - _subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable)); + _parser = FolderNotificationParser(id: appId, callback: _handleCallback); + _subscription = + RustStreamReceiver.listen((observable) => _parser?.parse(observable)); } - void _bservableCallback(FolderNotification ty, Either result) { + void _handleCallback( + FolderNotification ty, Either result) { switch (ty) { - case FolderNotification.AppViewsChanged: - if (_viewsChanged != null) { - result.fold( - (payload) { - final repeatedView = RepeatedViewPB.fromBuffer(payload); - _viewsChanged!(left(repeatedView.items)); - }, - (error) => _viewsChanged!(right(error)), - ); - } - break; case FolderNotification.AppUpdated: if (_updated != null) { result.fold( @@ -63,7 +53,6 @@ class AppListener { Future stop() async { _parser = null; await _subscription?.cancel(); - _viewsChanged = null; _updated = null; } } diff --git a/frontend/app_flowy/lib/workspace/application/menu/menu_bloc.dart b/frontend/app_flowy/lib/workspace/application/menu/menu_bloc.dart index debb1e71c3..5786998ef8 100644 --- a/frontend/app_flowy/lib/workspace/application/menu/menu_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/menu/menu_bloc.dart @@ -6,6 +6,8 @@ import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -13,16 +15,23 @@ part 'menu_bloc.freezed.dart'; class MenuBloc extends Bloc { final WorkspaceService _workspaceService; - final WorkspaceListener listener; - final String workspaceId; + final WorkspaceListener _listener; + final UserProfilePB user; + final WorkspacePB workspace; - MenuBloc({required this.workspaceId, required this.listener}) - : _workspaceService = WorkspaceService(workspaceId: workspaceId), - super(MenuState.initial()) { + MenuBloc({ + required this.user, + required this.workspace, + }) : _workspaceService = WorkspaceService(workspaceId: workspace.id), + _listener = WorkspaceListener( + user: user, + workspaceId: workspace.id, + ), + super(MenuState.initial(workspace)) { on((event, emit) async { await event.map( initial: (e) async { - listener.start(appsChanged: _handleAppsOrFail); + _listener.start(appsChanged: _handleAppsOrFail); await _fetchApps(emit); }, openPage: (e) async { @@ -55,7 +64,7 @@ class MenuBloc extends Bloc { @override Future close() async { - await listener.stop(); + await _listener.stop(); return super.close(); } @@ -110,8 +119,8 @@ class MenuState with _$MenuState { required Plugin plugin, }) = _MenuState; - factory MenuState.initial() => MenuState( - apps: [], + factory MenuState.initial(WorkspacePB workspace) => MenuState( + apps: workspace.apps.items, successOrFailure: left(unit), plugin: makePlugin(pluginType: PluginType.blank), ); diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/menu.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/menu.dart index 1796640d00..62bcc8084f 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/menu/menu.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/menu.dart @@ -51,8 +51,10 @@ class HomeMenu extends StatelessWidget { providers: [ BlocProvider( create: (context) { - final menuBloc = getIt( - param1: user, param2: workspaceSetting.workspace.id); + final menuBloc = MenuBloc( + user: user, + workspace: workspaceSetting.workspace, + ); menuBloc.add(const MenuEvent.initial()); return menuBloc; }, @@ -221,8 +223,7 @@ class MenuTopBar extends StatelessWidget { const Spacer(), Tooltip( richMessage: TextSpan(children: [ - TextSpan( - text: "${LocaleKeys.sideBar_closeSidebar.tr()}\n"), + TextSpan(text: "${LocaleKeys.sideBar_closeSidebar.tr()}\n"), TextSpan( text: Platform.isMacOS ? "⌘+\\" : "Ctrl+\\", style: const TextStyle(color: Colors.white60), diff --git a/frontend/app_flowy/test/bloc_test/menu_test/app_bloc_test.dart b/frontend/app_flowy/test/bloc_test/menu_test/app_bloc_test.dart index 22b1823fdd..7eb49437dd 100644 --- a/frontend/app_flowy/test/bloc_test/menu_test/app_bloc_test.dart +++ b/frontend/app_flowy/test/bloc_test/menu_test/app_bloc_test.dart @@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/board/board.dart'; import 'package:app_flowy/plugins/doc/document.dart'; import 'package:app_flowy/plugins/grid/grid.dart'; import 'package:app_flowy/workspace/application/app/app_bloc.dart'; +import 'package:app_flowy/workspace/application/menu/menu_view_section_bloc.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -125,4 +126,64 @@ void main() { }, ); }); + + group('$AppBloc', () { + late AppPB app; + setUpAll(() async { + app = await test.createTestApp(); + }); + blocTest( + "create documents' order test", + build: () => AppBloc(app: app)..add(const AppEvent.initial()), + act: (bloc) async { + bloc.add(AppEvent.createView("1", DocumentPluginBuilder())); + await blocResponseFuture(); + bloc.add(AppEvent.createView("2", DocumentPluginBuilder())); + await blocResponseFuture(); + bloc.add(AppEvent.createView("3", DocumentPluginBuilder())); + await blocResponseFuture(); + }, + wait: blocResponseDuration(), + verify: (bloc) { + assert(bloc.state.views[0].name == '1'); + assert(bloc.state.views[1].name == '2'); + assert(bloc.state.views[2].name == '3'); + }, + ); + }); + + group('$AppBloc', () { + late AppPB app; + setUpAll(() async { + app = await test.createTestApp(); + }); + blocTest( + "reorder documents", + build: () => AppBloc(app: app)..add(const AppEvent.initial()), + act: (bloc) async { + bloc.add(AppEvent.createView("1", DocumentPluginBuilder())); + await blocResponseFuture(); + bloc.add(AppEvent.createView("2", DocumentPluginBuilder())); + await blocResponseFuture(); + bloc.add(AppEvent.createView("3", DocumentPluginBuilder())); + await blocResponseFuture(); + + final appViewData = AppViewDataContext(appId: app.id); + appViewData.views = bloc.state.views; + final viewSectionBloc = ViewSectionBloc( + appViewData: appViewData, + )..add(const ViewSectionEvent.initial()); + await blocResponseFuture(); + + viewSectionBloc.add(const ViewSectionEvent.moveView(0, 2)); + await blocResponseFuture(); + }, + wait: blocResponseDuration(), + verify: (bloc) { + assert(bloc.state.views[0].name == '2'); + assert(bloc.state.views[1].name == '3'); + assert(bloc.state.views[2].name == '1'); + }, + ); + }); } diff --git a/frontend/app_flowy/test/bloc_test/menu_test/menu_bloc_test.dart b/frontend/app_flowy/test/bloc_test/menu_test/menu_bloc_test.dart new file mode 100644 index 0000000000..c8f38d9250 --- /dev/null +++ b/frontend/app_flowy/test/bloc_test/menu_test/menu_bloc_test.dart @@ -0,0 +1,66 @@ +import 'package:app_flowy/workspace/application/menu/menu_bloc.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../util.dart'; + +void main() { + late AppFlowyUnitTest test; + setUpAll(() async { + test = await AppFlowyUnitTest.ensureInitialized(); + }); + + group('$MenuBloc', () { + late MenuBloc menuBloc; + setUp(() async { + menuBloc = MenuBloc( + user: test.userProfile, + workspace: test.currentWorkspace, + )..add(const MenuEvent.initial()); + + await blocResponseFuture(); + }); + blocTest( + "assert initial apps is the build-in app", + build: () => menuBloc, + wait: blocResponseDuration(), + verify: (bloc) { + assert(bloc.state.apps.length == 1); + }, + ); + // + blocTest( + "create apps", + build: () => menuBloc, + act: (bloc) async { + bloc.add(const MenuEvent.createApp("App 1")); + await blocResponseFuture(); + bloc.add(const MenuEvent.createApp("App 2")); + await blocResponseFuture(); + bloc.add(const MenuEvent.createApp("App 3")); + }, + wait: blocResponseDuration(), + verify: (bloc) { + // apps[0] is the build-in app + assert(bloc.state.apps[1].name == 'App 1'); + assert(bloc.state.apps[2].name == 'App 2'); + assert(bloc.state.apps[3].name == 'App 3'); + }, + ); + blocTest( + "reorder apps", + build: () => menuBloc, + act: (bloc) async { + bloc.add(const MenuEvent.moveApp(1, 3)); + }, + wait: blocResponseDuration(), + verify: (bloc) { + assert(bloc.state.apps[1].name == 'App 2'); + assert(bloc.state.apps[2].name == 'App 3'); + assert(bloc.state.apps[3].name == 'App 1'); + }, + ); + }); + + // +} diff --git a/frontend/app_flowy/test/bloc_test/menu_test/trash_bloc_test.dart b/frontend/app_flowy/test/bloc_test/menu_test/trash_bloc_test.dart new file mode 100644 index 0000000000..11a7a97b03 --- /dev/null +++ b/frontend/app_flowy/test/bloc_test/menu_test/trash_bloc_test.dart @@ -0,0 +1,110 @@ +import 'package:app_flowy/plugins/doc/document.dart'; +import 'package:app_flowy/plugins/trash/application/trash_bloc.dart'; +import 'package:app_flowy/workspace/application/app/app_bloc.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../util.dart'; + +void main() { + late AppFlowyUnitTest test; + late AppPB app; + late AppBloc appBloc; + late List allViews; + setUpAll(() async { + test = await AppFlowyUnitTest.ensureInitialized(); + + /// Create a new app with three documents + app = await test.createTestApp(); + appBloc = AppBloc(app: app) + ..add(const AppEvent.initial()) + ..add(AppEvent.createView( + "Document 1", + DocumentPluginBuilder(), + )) + ..add(AppEvent.createView( + "Document 2", + DocumentPluginBuilder(), + )) + ..add( + AppEvent.createView( + "Document 3", + DocumentPluginBuilder(), + ), + ); + await blocResponseFuture(millisecond: 200); + allViews = [...appBloc.state.app.belongings.items]; + assert(allViews.length == 3); + }); + + group('$TrashBloc', () { + late TrashBloc trashBloc; + late ViewPB deletedView; + + setUpAll(() {}); + + setUp(() async { + trashBloc = TrashBloc()..add(const TrashEvent.initial()); + await blocResponseFuture(); + }); + + blocTest( + "delete view", + build: () => trashBloc, + act: (bloc) async { + deletedView = appBloc.state.app.belongings.items[0]; + appBloc.add(AppEvent.deleteView(deletedView.id)); + }, + wait: blocResponseDuration(), + verify: (bloc) { + assert(appBloc.state.app.belongings.items.length == 2); + assert(bloc.state.objects.length == 1); + assert(bloc.state.objects.first.id == deletedView.id); + }, + ); + + blocTest( + "delete all views", + build: () => trashBloc, + act: (bloc) async { + for (final view in appBloc.state.app.belongings.items) { + appBloc.add(AppEvent.deleteView(view.id)); + await blocResponseFuture(); + } + }, + wait: blocResponseDuration(), + verify: (bloc) { + assert(bloc.state.objects[0].id == allViews[0].id); + assert(bloc.state.objects[1].id == allViews[1].id); + assert(bloc.state.objects[2].id == allViews[2].id); + }, + ); + blocTest( + "put back", + build: () => trashBloc, + act: (bloc) async { + bloc.add(TrashEvent.putback(allViews[0].id)); + }, + wait: blocResponseDuration(), + verify: (bloc) { + assert(appBloc.state.app.belongings.items.length == 1); + assert(bloc.state.objects.length == 2); + }, + ); + blocTest( + "put back all", + build: () => trashBloc, + act: (bloc) async { + bloc.add(const TrashEvent.restoreAll()); + }, + wait: blocResponseDuration(), + verify: (bloc) { + assert(appBloc.state.app.belongings.items.length == 3); + assert(bloc.state.objects.isEmpty); + }, + ); + // + }); +} diff --git a/frontend/app_flowy/test/util.dart b/frontend/app_flowy/test/util.dart index 5549b4fd64..414f732b96 100644 --- a/frontend/app_flowy/test/util.dart +++ b/frontend/app_flowy/test/util.dart @@ -113,10 +113,10 @@ class FlowyTestApp implements EntryPoint { } } -Future blocResponseFuture() { - return Future.delayed(const Duration(milliseconds: 100)); +Future blocResponseFuture({int millisecond = 100}) { + return Future.delayed(Duration(milliseconds: millisecond)); } -Duration blocResponseDuration({int millseconds = 100}) { - return Duration(milliseconds: millseconds); +Duration blocResponseDuration({int milliseconds = 100}) { + return Duration(milliseconds: milliseconds); } diff --git a/frontend/rust-lib/flowy-folder/src/dart_notification.rs b/frontend/rust-lib/flowy-folder/src/dart_notification.rs index 2152abce48..efbdf113ea 100644 --- a/frontend/rust-lib/flowy-folder/src/dart_notification.rs +++ b/frontend/rust-lib/flowy-folder/src/dart_notification.rs @@ -12,7 +12,6 @@ pub(crate) enum FolderNotification { WorkspaceAppsChanged = 14, WorkspaceSetting = 15, AppUpdated = 21, - AppViewsChanged = 24, ViewUpdated = 31, ViewDeleted = 32, ViewRestored = 33, diff --git a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs b/frontend/rust-lib/flowy-folder/src/services/view/controller.rs index 67eda75caf..50f8ca6101 100644 --- a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs +++ b/frontend/rust-lib/flowy-folder/src/services/view/controller.rs @@ -1,5 +1,5 @@ pub use crate::entities::view::ViewDataFormatPB; -use crate::entities::{DeletedViewPB, ViewInfoPB, ViewLayoutTypePB}; +use crate::entities::{AppPB, DeletedViewPB, ViewInfoPB, ViewLayoutTypePB}; use crate::manager::{ViewDataProcessor, ViewDataProcessorMap}; use crate::{ dart_notification::{send_dart_notification, FolderNotification}, @@ -531,16 +531,15 @@ fn notify_views_changed<'a>( trash_controller: Arc, transaction: &'a (dyn FolderPersistenceTransaction + 'a), ) -> FlowyResult<()> { - let items: Vec = read_belonging_views_on_local(belong_to_id, trash_controller.clone(), transaction)? - .into_iter() - .map(|view_rev| view_rev.into()) - .collect(); - tracing::Span::current().record("view_count", &format!("{}", items.len()).as_str()); + let mut app_rev = transaction.read_app(belong_to_id)?; + let trash_ids = trash_controller.read_trash_ids(transaction)?; + app_rev.belongings.retain(|view| !trash_ids.contains(&view.id)); + let app: AppPB = app_rev.into(); - let repeated_view = RepeatedViewPB { items }; - send_dart_notification(belong_to_id, FolderNotification::AppViewsChanged) - .payload(repeated_view) + send_dart_notification(belong_to_id, FolderNotification::AppUpdated) + .payload(app) .send(); + Ok(()) }