diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/tar_bar_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/tar_bar_bloc.dart index 4b4697add2f25..815c846d23f93 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/tar_bar_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/tar_bar_bloc.dart @@ -1,5 +1,6 @@ import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_view.dart'; import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart'; +import 'package:appflowy/plugins/trash/application/trash_service.dart'; import 'package:appflowy/workspace/application/view/prelude.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy_backend/log.dart'; @@ -14,17 +15,38 @@ import 'database_view_service.dart'; part 'tar_bar_bloc.freezed.dart'; class GridTabBarBloc extends Bloc { + final TrashService _trashService; + final ViewListener _viewListener; + GridTabBarBloc({ bool isInlineView = false, required ViewPB view, - }) : super(GridTabBarState.initial(view)) { + }) : _trashService = TrashService(), + _viewListener = ViewListener(viewId: view.id), + super(GridTabBarState.initial(view)) { on( (event, emit) async { - event.when( + await event.when( initial: () { _listenInlineViewChanged(); _loadChildView(); }, + moveToTrash: () async { + emit(state.copyWith(isDeleted: true)); + }, + restore: () async { + emit(state.copyWith(isDeleted: false)); + }, + deletePermanently: () async { + final result = await _trashService.deleteViews([view.id]); + final forceClose = result.fold((l) => true, (r) => false); + emit(state.copyWith(forceClose: forceClose)); + }, + restorePage: () async { + final result = await _trashService.putback(view.id); + final isDeleted = result.fold((l) => false, (r) => true); + emit(state.copyWith(isDeleted: isDeleted)); + }, didLoadChildViews: (List childViews) { emit( state.copyWith( @@ -53,7 +75,11 @@ class GridTabBarBloc extends Bloc { deleteView: (String viewId) async { final result = await ViewBackendService.delete(viewId: viewId); result.fold( - (l) {}, + (l) { + emit( + state.copyWith(isDeleted: true), + ); + }, (r) => Log.error(r), ); }, @@ -130,6 +156,7 @@ class GridTabBarBloc extends Bloc { for (final tabBar in state.tabBars) { await state.tabBarControllerByViewId[tabBar.viewId]?.dispose(); } + await _viewListener.stop(); return super.close(); } @@ -143,6 +170,17 @@ class GridTabBarBloc extends Bloc { controller?.onViewChildViewChanged = (update) { add(GridTabBarEvent.didUpdateChildViews(update)); }; + + _viewListener.start( + onViewMoveToTrash: (r) { + r.swap().map((r) => add(const GridTabBarEvent.moveToTrash())); + }, + onViewDeleted: (r) { + r.swap().map((r) => add(const GridTabBarEvent.moveToTrash())); + }, + onViewRestored: (r) => + r.swap().map((r) => add(const GridTabBarEvent.restore())), + ); } /// Create tab bar controllers for the new views and return the updated map. @@ -201,6 +239,10 @@ class GridTabBarBloc extends Bloc { @freezed class GridTabBarEvent with _$GridTabBarEvent { const factory GridTabBarEvent.initial() = _Initial; + const factory GridTabBarEvent.moveToTrash() = MoveToTrash; + const factory GridTabBarEvent.restore() = Restore; + const factory GridTabBarEvent.restorePage() = RestorePage; + const factory GridTabBarEvent.deletePermanently() = DeletePermanently; const factory GridTabBarEvent.didLoadChildViews( List childViews, ) = _DidLoadChildViews; @@ -223,6 +265,8 @@ class GridTabBarState with _$GridTabBarState { required int selectedIndex, required List tabBars, required Map tabBarControllerByViewId, + required bool isDeleted, + required bool forceClose, }) = _GridTabBarState; factory GridTabBarState.initial(ViewPB view) { @@ -236,6 +280,8 @@ class GridTabBarState with _$GridTabBarState { view: view, ) }, + isDeleted: false, + forceClose: false, ); } } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/tab_bar_view.dart b/frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/tab_bar_view.dart index a32e706bf659c..0ef03464a6302 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/tab_bar_view.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/tab_bar_view.dart @@ -1,4 +1,5 @@ import 'package:appflowy/plugins/database_view/application/tar_bar_bloc.dart'; +import 'package:appflowy/plugins/database_view/banner.dart'; import 'package:appflowy/plugins/database_view/widgets/share_button.dart'; import 'package:appflowy/plugins/util.dart'; import 'package:appflowy/startup/plugin/plugin.dart'; @@ -82,11 +83,12 @@ class _DatabaseTabBarViewState extends State { }, ), ], - child: Column( - children: [ - BlocBuilder( - builder: (context, state) { - return ValueListenableBuilder( + child: BlocBuilder( + builder: (context, state) { + return Column( + children: [ + if (state.isDeleted) _buildBanner(context), + ValueListenableBuilder( valueListenable: state .tabBarControllerByViewId[state.parentView.id]! .controller @@ -105,32 +107,32 @@ class _DatabaseTabBarViewState extends State { ), ); }, - ); - }, - ), - BlocBuilder( - builder: (context, state) { - return pageSettingBarExtensionFromState(state); - }, - ), - Expanded( - child: BlocBuilder( - builder: (context, state) { - return PageView( + ), + pageSettingBarExtensionFromState(state), + Expanded( + child: PageView( pageSnapping: false, physics: const NeverScrollableScrollPhysics(), controller: _pageController, children: pageContentFromState(state), - ); - }, - ), - ), - ], + ), + ), + ], + ); + }, ), ), ); } + Widget _buildBanner(BuildContext context) { + final bloc = context.read(); + return DatabaseViewBanner( + onRestore: () => bloc.add(const GridTabBarEvent.restorePage()), + onDelete: () => bloc.add(const GridTabBarEvent.deletePermanently()), + ); + } + List pageContentFromState(GridTabBarState state) { return state.tabBars.map((tabBar) { final controller =