From 37f85cebdec7d602dbfcb878c641cd8b1edab65a Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 22 Sep 2022 13:08:48 +0800 Subject: [PATCH] chore: open next page when current page get deleted --- .../app_flowy/lib/plugins/blank/blank.dart | 11 +--- .../app_flowy/lib/plugins/board/board.dart | 28 ++++++--- .../board/presentation/board_page.dart | 5 +- .../app_flowy/lib/plugins/doc/document.dart | 59 ++++++++----------- .../lib/plugins/doc/document_page.dart | 16 ++--- frontend/app_flowy/lib/plugins/grid/grid.dart | 28 ++++++--- .../plugins/grid/presentation/grid_page.dart | 7 ++- .../app_flowy/lib/plugins/trash/trash.dart | 4 +- frontend/app_flowy/lib/plugins/util.dart | 44 ++++++++++++++ .../app_flowy/lib/startup/deps_resolver.dart | 2 +- .../app_flowy/lib/startup/plugin/plugin.dart | 46 +++++++-------- .../application/app/app_service.dart | 7 +-- .../application/home/home_service.dart | 14 +++++ .../menu/menu_view_section_bloc.dart | 17 ++++-- .../application/view/view_listener.dart | 41 ++++++++++--- .../workspace/workspace_service.dart | 9 ++- .../presentation/home/home_screen.dart | 48 +++++++++++++-- .../presentation/home/home_stack.dart | 37 +++++++----- .../presentation/home/menu/app/menu_app.dart | 13 +++- .../flowy-folder/src/dart_notification.rs | 1 + frontend/rust-lib/flowy-folder/src/manager.rs | 2 - .../src/services/view/controller.rs | 18 ++++-- .../src/services/view/event_handler.rs | 4 +- frontend/rust-lib/flowy-grid/src/manager.rs | 11 ---- .../flowy-sdk/src/deps_resolve/folder_deps.rs | 18 ------ 25 files changed, 312 insertions(+), 178 deletions(-) create mode 100644 frontend/app_flowy/lib/plugins/util.dart create mode 100644 frontend/app_flowy/lib/workspace/application/home/home_service.dart diff --git a/frontend/app_flowy/lib/plugins/blank/blank.dart b/frontend/app_flowy/lib/plugins/blank/blank.dart index 350a30ed50..7595adefd5 100644 --- a/frontend/app_flowy/lib/plugins/blank/blank.dart +++ b/frontend/app_flowy/lib/plugins/blank/blank.dart @@ -9,7 +9,7 @@ import 'package:app_flowy/startup/plugin/plugin.dart'; class BlankPluginBuilder extends PluginBuilder { @override Plugin build(dynamic data) { - return BlankPagePlugin(pluginType: pluginType); + return BlankPagePlugin(); } @override @@ -25,11 +25,6 @@ class BlankPluginConfig implements PluginConfig { } class BlankPagePlugin extends Plugin { - final PluginType _pluginType; - BlankPagePlugin({ - required PluginType pluginType, - }) : _pluginType = pluginType; - @override PluginDisplay get display => BlankPagePluginDisplay(); @@ -37,7 +32,7 @@ class BlankPagePlugin extends Plugin { PluginId get id => "BlankStack"; @override - PluginType get ty => _pluginType; + PluginType get ty => PluginType.blank; } class BlankPagePluginDisplay extends PluginDisplay with NavigationItem { @@ -46,7 +41,7 @@ class BlankPagePluginDisplay extends PluginDisplay with NavigationItem { FlowyText.medium(LocaleKeys.blankPageTitle.tr(), fontSize: 12); @override - Widget buildWidget() => const BlankPage(); + Widget buildWidget(PluginContext context) => const BlankPage(); @override List get navigationItems => [this]; diff --git a/frontend/app_flowy/lib/plugins/board/board.dart b/frontend/app_flowy/lib/plugins/board/board.dart index 213cc8bc3c..73fff5b639 100644 --- a/frontend/app_flowy/lib/plugins/board/board.dart +++ b/frontend/app_flowy/lib/plugins/board/board.dart @@ -1,3 +1,4 @@ +import 'package:app_flowy/plugins/util.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; @@ -35,34 +36,45 @@ class BoardPluginConfig implements PluginConfig { } class BoardPlugin extends Plugin { - final ViewPB _view; + @override + final ViewPluginNotifier notifier; final PluginType _pluginType; BoardPlugin({ required ViewPB view, required PluginType pluginType, }) : _pluginType = pluginType, - _view = view; + notifier = ViewPluginNotifier(view: view); @override - PluginDisplay get display => GridPluginDisplay(view: _view); + PluginDisplay get display => GridPluginDisplay(notifier: notifier); @override - PluginId get id => _view.id; + PluginId get id => notifier.view.id; @override PluginType get ty => _pluginType; } class GridPluginDisplay extends PluginDisplay { - final ViewPB _view; - GridPluginDisplay({required ViewPB view, Key? key}) : _view = view; + final ViewPluginNotifier notifier; + GridPluginDisplay({required this.notifier, Key? key}); + + ViewPB get view => notifier.view; @override - Widget get leftBarItem => ViewLeftBarItem(view: _view); + Widget get leftBarItem => ViewLeftBarItem(view: view); @override - Widget buildWidget() => BoardPage(view: _view); + Widget buildWidget(PluginContext context) { + notifier.isDeleted.addListener(() { + if (notifier.isDeleted.value) { + context.onDeleted(view); + } + }); + + return BoardPage(key: ValueKey(view.id), view: view); + } @override List get navigationItems => [this]; diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index 2a1762c3ed..b6d92e2781 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -31,7 +31,10 @@ import 'toolbar/board_toolbar.dart'; class BoardPage extends StatelessWidget { final ViewPB view; - BoardPage({required this.view, Key? key}) : super(key: ValueKey(view.id)); + BoardPage({ + required this.view, + Key? key, + }) : super(key: ValueKey(view.id)); @override Widget build(BuildContext context) { diff --git a/frontend/app_flowy/lib/plugins/doc/document.dart b/frontend/app_flowy/lib/plugins/doc/document.dart index 31488d3d2e..128eabfd20 100644 --- a/frontend/app_flowy/lib/plugins/doc/document.dart +++ b/frontend/app_flowy/lib/plugins/doc/document.dart @@ -1,10 +1,10 @@ library document_plugin; import 'package:app_flowy/generated/locale_keys.g.dart'; +import 'package:app_flowy/plugins/util.dart'; import 'package:app_flowy/startup/plugin/plugin.dart'; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/appearance.dart'; -import 'package:app_flowy/workspace/application/view/view_listener.dart'; import 'package:app_flowy/plugins/doc/application/share_bloc.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/home/toast.dart'; @@ -14,7 +14,6 @@ import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:clipboard/clipboard.dart'; import 'package:dartz/dartz.dart' as dartz; import 'package:easy_localization/easy_localization.dart'; -import 'package:flowy_infra/notifier.dart'; import 'package:flowy_infra/size.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/widget/rounded_button.dart'; @@ -48,63 +47,51 @@ class DocumentPluginBuilder extends PluginBuilder { ViewDataTypePB get dataType => ViewDataTypePB.Text; } -class DocumentPlugin implements Plugin { - late ViewPB _view; - ViewListener? _listener; +class DocumentPlugin extends Plugin { late PluginType _pluginType; - DocumentPlugin( - {required PluginType pluginType, required ViewPB view, Key? key}) - : _view = view { + @override + final ViewPluginNotifier notifier; + + DocumentPlugin({ + required PluginType pluginType, + required ViewPB view, + Key? key, + }) : notifier = ViewPluginNotifier(view: view) { _pluginType = pluginType; - _listener = getIt(param1: view); - _listener?.start(onViewUpdated: (result) { - result.fold( - (newView) { - _view = newView; - display.notifier!.value = _view.hashCode; - }, - (error) {}, - ); - }); } @override - void dispose() { - _listener?.stop(); - _listener = null; - } - - @override - PluginDisplay get display => DocumentPluginDisplay(view: _view); + PluginDisplay get display => DocumentPluginDisplay(notifier: notifier); @override PluginType get ty => _pluginType; @override - PluginId get id => _view.id; + PluginId get id => notifier.view.id; } -class DocumentPluginDisplay extends PluginDisplay with NavigationItem { - final PublishNotifier _displayNotifier = PublishNotifier(); - final ViewPB _view; +class DocumentPluginDisplay extends PluginDisplay with NavigationItem { + final ViewPluginNotifier notifier; + ViewPB get view => notifier.view; - DocumentPluginDisplay({required ViewPB view, Key? key}) : _view = view; + DocumentPluginDisplay({required this.notifier, Key? key}); @override - Widget buildWidget() => DocumentPage(view: _view, key: ValueKey(_view.id)); + Widget buildWidget(PluginContext context) => DocumentPage( + view: view, + onDeleted: () => context.onDeleted(view), + key: ValueKey(view.id), + ); @override - Widget get leftBarItem => ViewLeftBarItem(view: _view); + Widget get leftBarItem => ViewLeftBarItem(view: view); @override - Widget? get rightBarItem => DocumentShareButton(view: _view); + Widget? get rightBarItem => DocumentShareButton(view: view); @override List get navigationItems => [this]; - - @override - PublishNotifier? get notifier => _displayNotifier; } class DocumentShareButton extends StatelessWidget { diff --git a/frontend/app_flowy/lib/plugins/doc/document_page.dart b/frontend/app_flowy/lib/plugins/doc/document_page.dart index 7a7550ed2c..1d745982be 100644 --- a/frontend/app_flowy/lib/plugins/doc/document_page.dart +++ b/frontend/app_flowy/lib/plugins/doc/document_page.dart @@ -14,9 +14,14 @@ import 'application/doc_bloc.dart'; import 'styles.dart'; class DocumentPage extends StatefulWidget { + final VoidCallback onDeleted; final ViewPB view; - DocumentPage({Key? key, required this.view}) : super(key: ValueKey(view.id)); + DocumentPage({ + required this.view, + required this.onDeleted, + Key? key, + }) : super(key: ValueKey(view.id)); @override State createState() => _DocumentPageState(); @@ -49,7 +54,8 @@ class _DocumentPageState extends State { finish: (result) => result.successOrFail.fold( (_) { if (state.forceClose) { - return _renderAppPage(); + widget.onDeleted(); + return const SizedBox(); } else { return _renderDocument(context, state); } @@ -134,10 +140,4 @@ class _DocumentPageState extends State { ), ); } - - Widget _renderAppPage() { - return Container( - color: Colors.black, - ); - } } diff --git a/frontend/app_flowy/lib/plugins/grid/grid.dart b/frontend/app_flowy/lib/plugins/grid/grid.dart index f7d23d3891..45dabb6fb5 100644 --- a/frontend/app_flowy/lib/plugins/grid/grid.dart +++ b/frontend/app_flowy/lib/plugins/grid/grid.dart @@ -1,4 +1,5 @@ import 'package:app_flowy/generated/locale_keys.g.dart'; +import 'package:app_flowy/plugins/util.dart'; import 'package:app_flowy/startup/plugin/plugin.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart'; @@ -37,34 +38,45 @@ class GridPluginConfig implements PluginConfig { } class GridPlugin extends Plugin { - final ViewPB _view; + @override + final ViewPluginNotifier notifier; final PluginType _pluginType; GridPlugin({ required ViewPB view, required PluginType pluginType, }) : _pluginType = pluginType, - _view = view; + notifier = ViewPluginNotifier(view: view); @override - PluginDisplay get display => GridPluginDisplay(view: _view); + PluginDisplay get display => GridPluginDisplay(notifier: notifier); @override - PluginId get id => _view.id; + PluginId get id => notifier.view.id; @override PluginType get ty => _pluginType; } class GridPluginDisplay extends PluginDisplay { - final ViewPB _view; - GridPluginDisplay({required ViewPB view, Key? key}) : _view = view; + final ViewPluginNotifier notifier; + ViewPB get view => notifier.view; + + GridPluginDisplay({required this.notifier, Key? key}); @override - Widget get leftBarItem => ViewLeftBarItem(view: _view); + Widget get leftBarItem => ViewLeftBarItem(view: view); @override - Widget buildWidget() => GridPage(view: _view); + Widget buildWidget(PluginContext context) { + notifier.isDeleted.addListener(() { + if (notifier.isDeleted.value) { + context.onDeleted(view); + } + }); + + return GridPage(key: ValueKey(view.id), view: view); + } @override List get navigationItems => [this]; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart b/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart index a70fe840dd..38a437dee3 100755 --- a/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart @@ -29,8 +29,13 @@ import 'widgets/toolbar/grid_toolbar.dart'; class GridPage extends StatefulWidget { final ViewPB view; + final VoidCallback? onDeleted; - GridPage({Key? key, required this.view}) : super(key: ValueKey(view.id)); + GridPage({ + required this.view, + this.onDeleted, + Key? key, + }) : super(key: ValueKey(view.id)); @override State createState() => _GridPageState(); diff --git a/frontend/app_flowy/lib/plugins/trash/trash.dart b/frontend/app_flowy/lib/plugins/trash/trash.dart index f338040b28..efa4f0bfef 100644 --- a/frontend/app_flowy/lib/plugins/trash/trash.dart +++ b/frontend/app_flowy/lib/plugins/trash/trash.dart @@ -66,7 +66,9 @@ class TrashPluginDisplay extends PluginDisplay { Widget? get rightBarItem => null; @override - Widget buildWidget() => const TrashPage(key: ValueKey('TrashPage')); + Widget buildWidget(PluginContext context) => const TrashPage( + key: ValueKey('TrashPage'), + ); @override List get navigationItems => [this]; diff --git a/frontend/app_flowy/lib/plugins/util.dart b/frontend/app_flowy/lib/plugins/util.dart new file mode 100644 index 0000000000..9d3e5bf528 --- /dev/null +++ b/frontend/app_flowy/lib/plugins/util.dart @@ -0,0 +1,44 @@ +import 'package:app_flowy/startup/plugin/plugin.dart'; +import 'package:app_flowy/workspace/application/view/view_listener.dart'; +import 'package:flowy_sdk/log.dart'; +import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; +import 'package:flutter/material.dart'; + +class ViewPluginNotifier extends PluginNotifier { + final ViewListener? _viewListener; + ViewPB view; + + @override + final ValueNotifier isDeleted = ValueNotifier(false); + + @override + final ValueNotifier isDisplayChanged = ValueNotifier(0); + + ViewPluginNotifier({ + required this.view, + }) : _viewListener = ViewListener(view: view) { + _viewListener?.start(onViewUpdated: (result) { + result.fold( + (updatedView) { + view = updatedView; + isDisplayChanged.value = updatedView.hashCode; + }, + (err) => Log.error(err), + ); + }, onViewMoveToTrash: (result) { + result.fold( + (deletedView) { + isDeleted.value = true; + }, + (err) => Log.error(err), + ); + }); + } + + @override + void dispose() { + isDeleted.dispose(); + isDisplayChanged.dispose(); + _viewListener?.stop(); + } +} diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index cd3cc00ec6..7a53237dac 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -120,7 +120,7 @@ void _resolveFolderDeps(GetIt getIt) { getIt.registerFactoryParam( (app, _) => AppBloc( app: app, - appService: AppService(appId: app.id), + appService: AppService(), appListener: AppListener(appId: app.id), ), ); diff --git a/frontend/app_flowy/lib/startup/plugin/plugin.dart b/frontend/app_flowy/lib/startup/plugin/plugin.dart index 168be76359..9c65f804d3 100644 --- a/frontend/app_flowy/lib/startup/plugin/plugin.dart +++ b/frontend/app_flowy/lib/startup/plugin/plugin.dart @@ -3,7 +3,6 @@ library flowy_plugin; import 'package:app_flowy/startup/plugin/plugin.dart'; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; -import 'package:flowy_infra/notifier.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flutter/widgets.dart'; @@ -17,33 +16,29 @@ enum PluginType { board, } -// extension FlowyDefaultPluginExt on DefaultPlugin { -// int type() { -// switch (this) { -// case DefaultPlugin.editor: -// return 0; -// case DefaultPlugin.blank: -// return 1; -// case DefaultPlugin.trash: -// return 2; -// case DefaultPlugin.grid: -// return 3; -// case DefaultPlugin.board: -// return 4; -// } -// } -// } - -// typedef PluginType = int; typedef PluginId = String; -abstract class Plugin { +abstract class Plugin { PluginId get id; PluginDisplay get display; + PluginNotifier? get notifier => null; + PluginType get ty; + void dispose() { + notifier?.dispose(); + } +} + +abstract class PluginNotifier { + /// Notify if the plugin get deleted + ValueNotifier get isDeleted; + + /// Notify if the [PluginDisplay]'s content was changed + ValueNotifier get isDisplayChanged; + void dispose() {} } @@ -64,12 +59,17 @@ abstract class PluginConfig { bool get creatable => true; } -abstract class PluginDisplay with NavigationItem { +abstract class PluginDisplay with NavigationItem { List get navigationItems; - PublishNotifier? get notifier => null; + Widget buildWidget(PluginContext context); +} - Widget buildWidget(); +class PluginContext { + // calls when widget of the plugin get deleted + final Function(ViewPB) onDeleted; + + PluginContext({required this.onDeleted}); } void registerPlugin({required PluginBuilder builder, PluginConfig? config}) { diff --git a/frontend/app_flowy/lib/workspace/application/app/app_service.dart b/frontend/app_flowy/lib/workspace/application/app/app_service.dart index e2a6896066..9f64a68326 100644 --- a/frontend/app_flowy/lib/workspace/application/app/app_service.dart +++ b/frontend/app_flowy/lib/workspace/application/app/app_service.dart @@ -9,12 +9,7 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:app_flowy/startup/plugin/plugin.dart'; class AppService { - final String appId; - AppService({ - required this.appId, - }); - - Future> getAppDesc({required String appId}) { + Future> readApp({required String appId}) { final payload = AppIdPB.create()..value = appId; return FolderEventReadApp(payload).send(); diff --git a/frontend/app_flowy/lib/workspace/application/home/home_service.dart b/frontend/app_flowy/lib/workspace/application/home/home_service.dart new file mode 100644 index 0000000000..bf74f931c1 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/home/home_service.dart @@ -0,0 +1,14 @@ +import 'dart:async'; + +import 'package:dartz/dartz.dart'; +import 'package:flowy_sdk/dispatch/dispatch.dart'; +import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart'; + +class HomeService { + Future> readApp({required String appId}) { + final payload = AppIdPB.create()..value = appId; + + return FolderEventReadApp(payload).send(); + } +} diff --git a/frontend/app_flowy/lib/workspace/application/menu/menu_view_section_bloc.dart b/frontend/app_flowy/lib/workspace/application/menu/menu_view_section_bloc.dart index e70dfd179a..1b499636c2 100644 --- a/frontend/app_flowy/lib/workspace/application/menu/menu_view_section_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/menu/menu_view_section_bloc.dart @@ -17,7 +17,7 @@ class ViewSectionBloc extends Bloc { ViewSectionBloc({ required AppViewDataContext appViewData, - }) : _appService = AppService(appId: appViewData.appId), + }) : _appService = AppService(), _appViewData = appViewData, super(ViewSectionState.initial(appViewData)) { on((event, emit) async { @@ -59,7 +59,8 @@ class ViewSectionBloc extends Bloc { } } - Future _moveView(_MoveView value, Emitter emit) async { + Future _moveView( + _MoveView value, Emitter emit) async { if (value.fromIndex < state.views.length) { final viewId = state.views[value.fromIndex].id; final views = List.from(state.views); @@ -92,9 +93,12 @@ class ViewSectionBloc extends Bloc { @freezed class ViewSectionEvent with _$ViewSectionEvent { const factory ViewSectionEvent.initial() = _Initial; - const factory ViewSectionEvent.setSelectedView(ViewPB? view) = _SetSelectedView; - const factory ViewSectionEvent.moveView(int fromIndex, int toIndex) = _MoveView; - const factory ViewSectionEvent.didReceiveViewUpdated(List views) = _DidReceiveViewUpdated; + const factory ViewSectionEvent.setSelectedView(ViewPB? view) = + _SetSelectedView; + const factory ViewSectionEvent.moveView(int fromIndex, int toIndex) = + _MoveView; + const factory ViewSectionEvent.didReceiveViewUpdated(List views) = + _DidReceiveViewUpdated; } @freezed @@ -104,7 +108,8 @@ class ViewSectionState with _$ViewSectionState { ViewPB? selectedView, }) = _ViewSectionState; - factory ViewSectionState.initial(AppViewDataContext appViewData) => ViewSectionState( + factory ViewSectionState.initial(AppViewDataContext appViewData) => + ViewSectionState( views: appViewData.views, selectedView: appViewData.selectedView, ); diff --git a/frontend/app_flowy/lib/workspace/application/view/view_listener.dart b/frontend/app_flowy/lib/workspace/application/view/view_listener.dart index 2b080ec194..7629b71edc 100644 --- a/frontend/app_flowy/lib/workspace/application/view/view_listener.dart +++ b/frontend/app_flowy/lib/workspace/application/view/view_listener.dart @@ -9,15 +9,21 @@ import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart'; import 'package:flowy_sdk/rust_stream.dart'; import 'package:flowy_infra/notifier.dart'; +// Delete the view from trash, which means the view was deleted permanently typedef DeleteViewNotifyValue = Either; +// The view get updated typedef UpdateViewNotifiedValue = Either; +// Restore the view from trash typedef RestoreViewNotifiedValue = Either; +// Move the view to trash +typedef MoveToTrashNotifiedValue = Either; class ViewListener { StreamSubscription? _subscription; - final PublishNotifier _updatedViewNotifier = PublishNotifier(); - final PublishNotifier _deletedNotifier = PublishNotifier(); - final PublishNotifier _restoredNotifier = PublishNotifier(); + final _updatedViewNotifier = PublishNotifier(); + final _deletedNotifier = PublishNotifier(); + final _restoredNotifier = PublishNotifier(); + final _moveToTrashNotifier = PublishNotifier(); FolderNotificationParser? _parser; ViewPB view; @@ -29,6 +35,7 @@ class ViewListener { void Function(UpdateViewNotifiedValue)? onViewUpdated, void Function(DeleteViewNotifyValue)? onViewDeleted, void Function(RestoreViewNotifiedValue)? onViewRestored, + void Function(MoveToTrashNotifiedValue)? onViewMoveToTrash, }) { if (onViewUpdated != null) { _updatedViewNotifier.addListener(() { @@ -48,6 +55,12 @@ class ViewListener { }); } + if (onViewMoveToTrash != null) { + _moveToTrashNotifier.addListener(() { + onViewMoveToTrash(_moveToTrashNotifier.currentValue!); + }); + } + _parser = FolderNotificationParser( id: view.id, callback: (ty, result) { @@ -55,29 +68,41 @@ class ViewListener { }, ); - _subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable)); + _subscription = + RustStreamReceiver.listen((observable) => _parser?.parse(observable)); } - void _handleObservableType(FolderNotification ty, Either result) { + void _handleObservableType( + FolderNotification ty, Either result) { switch (ty) { case FolderNotification.ViewUpdated: result.fold( - (payload) => _updatedViewNotifier.value = left(ViewPB.fromBuffer(payload)), + (payload) => + _updatedViewNotifier.value = left(ViewPB.fromBuffer(payload)), (error) => _updatedViewNotifier.value = right(error), ); break; case FolderNotification.ViewDeleted: result.fold( - (payload) => _deletedNotifier.value = left(ViewPB.fromBuffer(payload)), + (payload) => + _deletedNotifier.value = left(ViewPB.fromBuffer(payload)), (error) => _deletedNotifier.value = right(error), ); break; case FolderNotification.ViewRestored: result.fold( - (payload) => _restoredNotifier.value = left(ViewPB.fromBuffer(payload)), + (payload) => + _restoredNotifier.value = left(ViewPB.fromBuffer(payload)), (error) => _restoredNotifier.value = right(error), ); break; + case FolderNotification.ViewMoveToTrash: + result.fold( + (payload) => + _moveToTrashNotifier.value = left(ViewIdPB.fromBuffer(payload)), + (error) => _moveToTrashNotifier.value = right(error), + ); + break; default: break; } diff --git a/frontend/app_flowy/lib/workspace/application/workspace/workspace_service.dart b/frontend/app_flowy/lib/workspace/application/workspace/workspace_service.dart index 4f68d4776a..295b7c3af7 100644 --- a/frontend/app_flowy/lib/workspace/application/workspace/workspace_service.dart +++ b/frontend/app_flowy/lib/workspace/application/workspace/workspace_service.dart @@ -5,7 +5,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart' show MoveFolderItemPayloadPB, MoveFolderItemType; +import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart' + show MoveFolderItemPayloadPB, MoveFolderItemType; import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; @@ -15,7 +16,8 @@ class WorkspaceService { WorkspaceService({ required this.workspaceId, }); - Future> createApp({required String name, required String desc}) { + Future> createApp( + {required String name, required String desc}) { final payload = CreateAppPayloadPB.create() ..name = name ..workspaceId = workspaceId @@ -31,7 +33,8 @@ class WorkspaceService { assert(workspaces.items.length == 1); if (workspaces.items.isEmpty) { - return right(FlowyError.create()..msg = LocaleKeys.workspace_notFoundError.tr()); + return right(FlowyError.create() + ..msg = LocaleKeys.workspace_notFoundError.tr()); } else { return left(workspaces.items[0]); } diff --git a/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart b/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart index aac9f214bc..3e98b34d37 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart @@ -1,5 +1,7 @@ +import 'package:app_flowy/plugins/blank/blank.dart'; import 'package:app_flowy/startup/plugin/plugin.dart'; import 'package:app_flowy/workspace/application/home/home_bloc.dart'; +import 'package:app_flowy/workspace/application/home/home_service.dart'; import 'package:app_flowy/workspace/presentation/home/hotkeys.dart'; import 'package:app_flowy/workspace/application/view/view_ext.dart'; @@ -78,7 +80,7 @@ class _HomeScreenState extends State { return FlowyContainer( Theme.of(context).colorScheme.surface, // Colors.white, - child: _buildBody(state), + child: _buildBody(context, state), ); }, ), @@ -87,12 +89,16 @@ class _HomeScreenState extends State { ); } - Widget _buildBody(HomeState state) { + Widget _buildBody(BuildContext context, HomeState state) { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { final layout = HomeLayout(context, constraints, state.forceCollapse); final homeStack = HomeStack( layout: layout, + delegate: HomeScreenStackAdaptor( + buildContext: context, + homeState: state, + ), ); final menu = _buildHomeMenu( layout: layout, @@ -132,7 +138,7 @@ class _HomeScreenState extends State { getIt().setPlugin(plugin); } - HomeMenu homeMenu = HomeMenu( + final homeMenu = HomeMenu( user: widget.user, workspaceSetting: workspaceSetting, collapsedNotifier: getIt().collapsedNotifier, @@ -148,7 +154,6 @@ class _HomeScreenState extends State { return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu)); } - Widget _buildEditPanel( {required HomeState homeState, required BuildContext context, @@ -245,3 +250,38 @@ class _HomeScreenState extends State { ); } } + +class HomeScreenStackAdaptor extends HomeStackDelegate { + final BuildContext buildContext; + final HomeState homeState; + + HomeScreenStackAdaptor({ + required this.buildContext, + required this.homeState, + }); + + @override + void didDeleteStackWidget(ViewPB view) { + final homeService = HomeService(); + homeService.readApp(appId: view.appId).then((result) { + result.fold( + (appPB) { + final List views = appPB.belongings.items; + if (views.isNotEmpty) { + final lastView = views.last; + final plugin = makePlugin( + pluginType: lastView.pluginType, + data: lastView, + ); + getIt().latestOpenView = lastView; + getIt().setPlugin(plugin); + } else { + getIt().latestOpenView = null; + getIt().setPlugin(BlankPagePlugin()); + } + }, + (err) => Log.error(err), + ); + }); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart b/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart index eed9eb6f4a..a26565b566 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart @@ -2,7 +2,7 @@ import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/plugins/blank/blank.dart'; import 'package:app_flowy/workspace/presentation/home/toast.dart'; import 'package:flowy_infra/theme.dart'; -import 'package:flowy_sdk/log.dart'; +import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:time/time.dart'; @@ -17,14 +17,21 @@ import 'home_layout.dart'; typedef NavigationCallback = void Function(String id); -class HomeStack extends StatelessWidget { - const HomeStack({Key? key, required this.layout}) : super(key: key); +abstract class HomeStackDelegate { + void didDeleteStackWidget(ViewPB view); +} +class HomeStack extends StatelessWidget { + final HomeStackDelegate delegate; final HomeLayout layout; + const HomeStack({ + required this.delegate, + required this.layout, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { - Log.info('HomePage build'); final theme = context.watch(); return Column( mainAxisAlignment: MainAxisAlignment.start, @@ -34,7 +41,9 @@ class HomeStack extends StatelessWidget { child: Container( color: theme.surface, child: FocusTraversalGroup( - child: getIt().stackWidget(), + child: getIt().stackWidget(onDeleted: (view) { + delegate.didDeleteStackWidget(view); + }), ), ), ), @@ -114,18 +123,18 @@ class HomeStackNotifier extends ChangeNotifier { return; } - _plugin.display.notifier?.removeListener(notifyListeners); + _plugin.notifier?.isDisplayChanged.addListener(notifyListeners); _plugin.dispose(); _plugin = newPlugin; - _plugin.display.notifier?.addListener(notifyListeners); + _plugin.notifier?.isDisplayChanged.removeListener(notifyListeners); notifyListeners(); } Plugin get plugin => _plugin; } -// HomeStack is initialized as singleton to controll the page stack. +// HomeStack is initialized as singleton to control the page stack. class HomeStackManager { final HomeStackNotifier _notifier = HomeStackNotifier(); HomeStackManager(); @@ -140,7 +149,9 @@ class HomeStackManager { _notifier.plugin = newPlugin; } - void setStackWithId(String id) {} + void setStackWithId(String id) { + // Navigate to the page with id + } Widget stackTopBar({required HomeLayout layout}) { return MultiProvider( @@ -156,18 +167,16 @@ class HomeStackManager { ); } - Widget stackWidget() { + Widget stackWidget({required Function(ViewPB) onDeleted}) { return MultiProvider( - providers: [ - ChangeNotifierProvider.value(value: _notifier), - ], + providers: [ChangeNotifierProvider.value(value: _notifier)], child: Consumer(builder: (ctx, HomeStackNotifier notifier, child) { return FadingIndexedStack( index: getIt().indexOf(notifier.plugin.ty), children: getIt().supportPluginTypes.map((pluginType) { if (pluginType == notifier.plugin.ty) { return notifier.plugin.display - .buildWidget() + .buildWidget(PluginContext(onDeleted: onDeleted)) .padding(horizontal: 40, vertical: 28); } else { return const BlankPage(); diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/menu_app.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/menu_app.dart index e636c87d9a..7c2e2e4d87 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/menu_app.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/menu_app.dart @@ -42,7 +42,12 @@ class _MenuAppState extends State { listeners: [ BlocListener( listenWhen: (p, c) => p.latestCreatedView != c.latestCreatedView, - listener: (context, state) => getIt().latestOpenView = state.latestCreatedView, + listener: (context, state) { + if (state.latestCreatedView != null) { + getIt().latestOpenView = + state.latestCreatedView; + } + }, ), BlocListener( listenWhen: (p, c) => p.views != c.views, @@ -65,7 +70,8 @@ class _MenuAppState extends State { ); } - ExpandableNotifier expandableWrapper(BuildContext context, AppViewDataContext viewDataContext) { + ExpandableNotifier expandableWrapper( + BuildContext context, AppViewDataContext viewDataContext) { return ExpandableNotifier( controller: viewDataContext.expandController, child: ScrollOnExpand( @@ -83,7 +89,8 @@ class _MenuAppState extends State { hasIcon: false, ), header: ChangeNotifierProvider.value( - value: Provider.of(context, listen: true), + value: + Provider.of(context, listen: true), child: MenuAppHeader(widget.app), ), expanded: ViewSection(appViewData: viewDataContext), diff --git a/frontend/rust-lib/flowy-folder/src/dart_notification.rs b/frontend/rust-lib/flowy-folder/src/dart_notification.rs index c062bd4a70..2152abce48 100644 --- a/frontend/rust-lib/flowy-folder/src/dart_notification.rs +++ b/frontend/rust-lib/flowy-folder/src/dart_notification.rs @@ -16,6 +16,7 @@ pub(crate) enum FolderNotification { ViewUpdated = 31, ViewDeleted = 32, ViewRestored = 33, + ViewMoveToTrash = 34, UserUnauthorized = 100, TrashUpdated = 1000, } diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index 008476a397..25e3b1f15b 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -251,8 +251,6 @@ pub trait ViewDataProcessor { fn create_container(&self, user_id: &str, view_id: &str, delta_data: Bytes) -> FutureResult<(), FlowyError>; - fn delete_container(&self, view_id: &str) -> FutureResult<(), FlowyError>; - fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError>; fn get_delta_data(&self, view_id: &str) -> FutureResult; 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 ab0783cec0..944a1b68db 100644 --- a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs +++ b/frontend/rust-lib/flowy-folder/src/services/view/controller.rs @@ -125,7 +125,7 @@ impl ViewController { } #[tracing::instrument(level = "debug", skip(self, view_id), fields(view_id = %view_id.value), err)] - pub(crate) async fn read_view_info(&self, view_id: ViewIdPB) -> Result { + pub(crate) async fn read_view_pb(&self, view_id: ViewIdPB) -> Result { let view_info = self .persistence .begin_transaction(|transaction| { @@ -179,14 +179,20 @@ impl ViewController { } #[tracing::instrument(level = "debug", skip(self,params), fields(doc_id = %params.value), err)] - pub(crate) async fn delete_view(&self, params: TextBlockIdPB) -> Result<(), FlowyError> { - if let Some(view_id) = KV::get_str(LATEST_VIEW_ID) { - if view_id == params.value { + pub(crate) async fn move_view_to_trash(&self, params: TextBlockIdPB) -> Result<(), FlowyError> { + let view_id = params.value; + if let Some(latest_view_id) = KV::get_str(LATEST_VIEW_ID) { + if latest_view_id == view_id { let _ = KV::remove(LATEST_VIEW_ID); } } - let processor = self.get_data_processor_from_view_id(¶ms.value).await?; - let _ = processor.delete_container(¶ms.value).await?; + let view_id_pb = ViewIdPB::from(view_id.as_str()); + send_dart_notification(&view_id, FolderNotification::ViewMoveToTrash) + .payload(view_id_pb) + .send(); + + let processor = self.get_data_processor_from_view_id(&view_id).await?; + let _ = processor.close_container(&view_id).await?; Ok(()) } diff --git a/frontend/rust-lib/flowy-folder/src/services/view/event_handler.rs b/frontend/rust-lib/flowy-folder/src/services/view/event_handler.rs index 925ece6ba2..be7d61d9fe 100644 --- a/frontend/rust-lib/flowy-folder/src/services/view/event_handler.rs +++ b/frontend/rust-lib/flowy-folder/src/services/view/event_handler.rs @@ -40,7 +40,7 @@ pub(crate) async fn read_view_info_handler( controller: AppData>, ) -> DataResult { let view_id: ViewIdPB = data.into_inner(); - let view_info = controller.read_view_info(view_id.clone()).await?; + let view_info = controller.read_view_pb(view_id.clone()).await?; data_result(view_info) } @@ -62,7 +62,7 @@ pub(crate) async fn delete_view_handler( ) -> Result<(), FlowyError> { let params: RepeatedViewIdPB = data.into_inner(); for view_id in ¶ms.items { - let _ = view_controller.delete_view(view_id.into()).await; + let _ = view_controller.move_view_to_trash(view_id.into()).await; } let trash = view_controller diff --git a/frontend/rust-lib/flowy-grid/src/manager.rs b/frontend/rust-lib/flowy-grid/src/manager.rs index 12b94ff8db..bf094b2f9d 100644 --- a/frontend/rust-lib/flowy-grid/src/manager.rs +++ b/frontend/rust-lib/flowy-grid/src/manager.rs @@ -109,17 +109,6 @@ impl GridManager { Ok(()) } - #[tracing::instrument(level = "debug", skip(self, grid_id), fields(doc_id), err)] - pub async fn delete_grid>(&self, grid_id: T) -> FlowyResult<()> { - let grid_id = grid_id.as_ref(); - tracing::Span::current().record("grid_id", &grid_id); - self.grid_editors.remove(grid_id); - self.task_scheduler.write().await.unregister_handler(grid_id); - Ok(()) - } - - // pub fn update_grid_info() - // #[tracing::instrument(level = "debug", skip(self), err)] pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult> { match self.grid_editors.get(grid_id) { diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs index 79b5468a56..d373c6dc2d 100644 --- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs @@ -152,15 +152,6 @@ impl ViewDataProcessor for TextBlockViewDataProcessor { }) } - fn delete_container(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - let _ = manager.close_text_editor(view_id)?; - Ok(()) - }) - } - fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError> { let manager = self.0.clone(); let view_id = view_id.to_string(); @@ -230,15 +221,6 @@ impl ViewDataProcessor for GridViewDataProcessor { }) } - fn delete_container(&self, view_id: &str) -> FutureResult<(), FlowyError> { - let grid_manager = self.0.clone(); - let view_id = view_id.to_string(); - FutureResult::new(async move { - let _ = grid_manager.delete_grid(view_id).await?; - Ok(()) - }) - } - fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError> { let grid_manager = self.0.clone(); let view_id = view_id.to_string();