chore: open next page when current page get deleted

This commit is contained in:
appflowy 2022-09-22 13:08:48 +08:00
parent bdf4e60b48
commit 37f85cebde
25 changed files with 312 additions and 178 deletions

View File

@ -9,7 +9,7 @@ import 'package:app_flowy/startup/plugin/plugin.dart';
class BlankPluginBuilder extends PluginBuilder { class BlankPluginBuilder extends PluginBuilder {
@override @override
Plugin build(dynamic data) { Plugin build(dynamic data) {
return BlankPagePlugin(pluginType: pluginType); return BlankPagePlugin();
} }
@override @override
@ -25,11 +25,6 @@ class BlankPluginConfig implements PluginConfig {
} }
class BlankPagePlugin extends Plugin { class BlankPagePlugin extends Plugin {
final PluginType _pluginType;
BlankPagePlugin({
required PluginType pluginType,
}) : _pluginType = pluginType;
@override @override
PluginDisplay get display => BlankPagePluginDisplay(); PluginDisplay get display => BlankPagePluginDisplay();
@ -37,7 +32,7 @@ class BlankPagePlugin extends Plugin {
PluginId get id => "BlankStack"; PluginId get id => "BlankStack";
@override @override
PluginType get ty => _pluginType; PluginType get ty => PluginType.blank;
} }
class BlankPagePluginDisplay extends PluginDisplay with NavigationItem { class BlankPagePluginDisplay extends PluginDisplay with NavigationItem {
@ -46,7 +41,7 @@ class BlankPagePluginDisplay extends PluginDisplay with NavigationItem {
FlowyText.medium(LocaleKeys.blankPageTitle.tr(), fontSize: 12); FlowyText.medium(LocaleKeys.blankPageTitle.tr(), fontSize: 12);
@override @override
Widget buildWidget() => const BlankPage(); Widget buildWidget(PluginContext context) => const BlankPage();
@override @override
List<NavigationItem> get navigationItems => [this]; List<NavigationItem> get navigationItems => [this];

View File

@ -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/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart'; import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
@ -35,34 +36,45 @@ class BoardPluginConfig implements PluginConfig {
} }
class BoardPlugin extends Plugin { class BoardPlugin extends Plugin {
final ViewPB _view; @override
final ViewPluginNotifier notifier;
final PluginType _pluginType; final PluginType _pluginType;
BoardPlugin({ BoardPlugin({
required ViewPB view, required ViewPB view,
required PluginType pluginType, required PluginType pluginType,
}) : _pluginType = pluginType, }) : _pluginType = pluginType,
_view = view; notifier = ViewPluginNotifier(view: view);
@override @override
PluginDisplay get display => GridPluginDisplay(view: _view); PluginDisplay get display => GridPluginDisplay(notifier: notifier);
@override @override
PluginId get id => _view.id; PluginId get id => notifier.view.id;
@override @override
PluginType get ty => _pluginType; PluginType get ty => _pluginType;
} }
class GridPluginDisplay extends PluginDisplay { class GridPluginDisplay extends PluginDisplay {
final ViewPB _view; final ViewPluginNotifier notifier;
GridPluginDisplay({required ViewPB view, Key? key}) : _view = view; GridPluginDisplay({required this.notifier, Key? key});
ViewPB get view => notifier.view;
@override @override
Widget get leftBarItem => ViewLeftBarItem(view: _view); Widget get leftBarItem => ViewLeftBarItem(view: view);
@override @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 @override
List<NavigationItem> get navigationItems => [this]; List<NavigationItem> get navigationItems => [this];

View File

@ -31,7 +31,10 @@ import 'toolbar/board_toolbar.dart';
class BoardPage extends StatelessWidget { class BoardPage extends StatelessWidget {
final ViewPB view; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,10 +1,10 @@
library document_plugin; library document_plugin;
import 'package:app_flowy/generated/locale_keys.g.dart'; 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/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/appearance.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/plugins/doc/application/share_bloc.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/home/toast.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:clipboard/clipboard.dart';
import 'package:dartz/dartz.dart' as dartz; import 'package:dartz/dartz.dart' as dartz;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:flowy_infra/size.dart'; import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/widget/rounded_button.dart'; import 'package:flowy_infra_ui/widget/rounded_button.dart';
@ -48,63 +47,51 @@ class DocumentPluginBuilder extends PluginBuilder {
ViewDataTypePB get dataType => ViewDataTypePB.Text; ViewDataTypePB get dataType => ViewDataTypePB.Text;
} }
class DocumentPlugin implements Plugin { class DocumentPlugin extends Plugin<int> {
late ViewPB _view;
ViewListener? _listener;
late PluginType _pluginType; late PluginType _pluginType;
DocumentPlugin( @override
{required PluginType pluginType, required ViewPB view, Key? key}) final ViewPluginNotifier notifier;
: _view = view {
DocumentPlugin({
required PluginType pluginType,
required ViewPB view,
Key? key,
}) : notifier = ViewPluginNotifier(view: view) {
_pluginType = pluginType; _pluginType = pluginType;
_listener = getIt<ViewListener>(param1: view);
_listener?.start(onViewUpdated: (result) {
result.fold(
(newView) {
_view = newView;
display.notifier!.value = _view.hashCode;
},
(error) {},
);
});
} }
@override @override
void dispose() { PluginDisplay get display => DocumentPluginDisplay(notifier: notifier);
_listener?.stop();
_listener = null;
}
@override
PluginDisplay<int> get display => DocumentPluginDisplay(view: _view);
@override @override
PluginType get ty => _pluginType; PluginType get ty => _pluginType;
@override @override
PluginId get id => _view.id; PluginId get id => notifier.view.id;
} }
class DocumentPluginDisplay extends PluginDisplay<int> with NavigationItem { class DocumentPluginDisplay extends PluginDisplay with NavigationItem {
final PublishNotifier<int> _displayNotifier = PublishNotifier<int>(); final ViewPluginNotifier notifier;
final ViewPB _view; ViewPB get view => notifier.view;
DocumentPluginDisplay({required ViewPB view, Key? key}) : _view = view; DocumentPluginDisplay({required this.notifier, Key? key});
@override @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 @override
Widget get leftBarItem => ViewLeftBarItem(view: _view); Widget get leftBarItem => ViewLeftBarItem(view: view);
@override @override
Widget? get rightBarItem => DocumentShareButton(view: _view); Widget? get rightBarItem => DocumentShareButton(view: view);
@override @override
List<NavigationItem> get navigationItems => [this]; List<NavigationItem> get navigationItems => [this];
@override
PublishNotifier<int>? get notifier => _displayNotifier;
} }
class DocumentShareButton extends StatelessWidget { class DocumentShareButton extends StatelessWidget {

View File

@ -14,9 +14,14 @@ import 'application/doc_bloc.dart';
import 'styles.dart'; import 'styles.dart';
class DocumentPage extends StatefulWidget { class DocumentPage extends StatefulWidget {
final VoidCallback onDeleted;
final ViewPB view; 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 @override
State<DocumentPage> createState() => _DocumentPageState(); State<DocumentPage> createState() => _DocumentPageState();
@ -49,7 +54,8 @@ class _DocumentPageState extends State<DocumentPage> {
finish: (result) => result.successOrFail.fold( finish: (result) => result.successOrFail.fold(
(_) { (_) {
if (state.forceClose) { if (state.forceClose) {
return _renderAppPage(); widget.onDeleted();
return const SizedBox();
} else { } else {
return _renderDocument(context, state); return _renderDocument(context, state);
} }
@ -134,10 +140,4 @@ class _DocumentPageState extends State<DocumentPage> {
), ),
); );
} }
Widget _renderAppPage() {
return Container(
color: Colors.black,
);
}
} }

View File

@ -1,4 +1,5 @@
import 'package:app_flowy/generated/locale_keys.g.dart'; 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/plugin/plugin.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart'; import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
@ -37,34 +38,45 @@ class GridPluginConfig implements PluginConfig {
} }
class GridPlugin extends Plugin { class GridPlugin extends Plugin {
final ViewPB _view; @override
final ViewPluginNotifier notifier;
final PluginType _pluginType; final PluginType _pluginType;
GridPlugin({ GridPlugin({
required ViewPB view, required ViewPB view,
required PluginType pluginType, required PluginType pluginType,
}) : _pluginType = pluginType, }) : _pluginType = pluginType,
_view = view; notifier = ViewPluginNotifier(view: view);
@override @override
PluginDisplay get display => GridPluginDisplay(view: _view); PluginDisplay get display => GridPluginDisplay(notifier: notifier);
@override @override
PluginId get id => _view.id; PluginId get id => notifier.view.id;
@override @override
PluginType get ty => _pluginType; PluginType get ty => _pluginType;
} }
class GridPluginDisplay extends PluginDisplay { class GridPluginDisplay extends PluginDisplay {
final ViewPB _view; final ViewPluginNotifier notifier;
GridPluginDisplay({required ViewPB view, Key? key}) : _view = view; ViewPB get view => notifier.view;
GridPluginDisplay({required this.notifier, Key? key});
@override @override
Widget get leftBarItem => ViewLeftBarItem(view: _view); Widget get leftBarItem => ViewLeftBarItem(view: view);
@override @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 @override
List<NavigationItem> get navigationItems => [this]; List<NavigationItem> get navigationItems => [this];

View File

@ -29,8 +29,13 @@ import 'widgets/toolbar/grid_toolbar.dart';
class GridPage extends StatefulWidget { class GridPage extends StatefulWidget {
final ViewPB view; 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 @override
State<GridPage> createState() => _GridPageState(); State<GridPage> createState() => _GridPageState();

View File

@ -66,7 +66,9 @@ class TrashPluginDisplay extends PluginDisplay {
Widget? get rightBarItem => null; Widget? get rightBarItem => null;
@override @override
Widget buildWidget() => const TrashPage(key: ValueKey('TrashPage')); Widget buildWidget(PluginContext context) => const TrashPage(
key: ValueKey('TrashPage'),
);
@override @override
List<NavigationItem> get navigationItems => [this]; List<NavigationItem> get navigationItems => [this];

View File

@ -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<bool> isDeleted = ValueNotifier(false);
@override
final ValueNotifier<int> 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();
}
}

View File

@ -120,7 +120,7 @@ void _resolveFolderDeps(GetIt getIt) {
getIt.registerFactoryParam<AppBloc, AppPB, void>( getIt.registerFactoryParam<AppBloc, AppPB, void>(
(app, _) => AppBloc( (app, _) => AppBloc(
app: app, app: app,
appService: AppService(appId: app.id), appService: AppService(),
appListener: AppListener(appId: app.id), appListener: AppListener(appId: app.id),
), ),
); );

View File

@ -3,7 +3,6 @@ library flowy_plugin;
import 'package:app_flowy/startup/plugin/plugin.dart'; import 'package:app_flowy/startup/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.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:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -17,33 +16,29 @@ enum PluginType {
board, 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; typedef PluginId = String;
abstract class Plugin { abstract class Plugin<T> {
PluginId get id; PluginId get id;
PluginDisplay get display; PluginDisplay get display;
PluginNotifier? get notifier => null;
PluginType get ty; PluginType get ty;
void dispose() {
notifier?.dispose();
}
}
abstract class PluginNotifier {
/// Notify if the plugin get deleted
ValueNotifier<bool> get isDeleted;
/// Notify if the [PluginDisplay]'s content was changed
ValueNotifier<int> get isDisplayChanged;
void dispose() {} void dispose() {}
} }
@ -64,12 +59,17 @@ abstract class PluginConfig {
bool get creatable => true; bool get creatable => true;
} }
abstract class PluginDisplay<T> with NavigationItem { abstract class PluginDisplay with NavigationItem {
List<NavigationItem> get navigationItems; List<NavigationItem> get navigationItems;
PublishNotifier<T>? 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}) { void registerPlugin({required PluginBuilder builder, PluginConfig? config}) {

View File

@ -9,12 +9,7 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:app_flowy/startup/plugin/plugin.dart'; import 'package:app_flowy/startup/plugin/plugin.dart';
class AppService { class AppService {
final String appId; Future<Either<AppPB, FlowyError>> readApp({required String appId}) {
AppService({
required this.appId,
});
Future<Either<AppPB, FlowyError>> getAppDesc({required String appId}) {
final payload = AppIdPB.create()..value = appId; final payload = AppIdPB.create()..value = appId;
return FolderEventReadApp(payload).send(); return FolderEventReadApp(payload).send();

View File

@ -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<Either<AppPB, FlowyError>> readApp({required String appId}) {
final payload = AppIdPB.create()..value = appId;
return FolderEventReadApp(payload).send();
}
}

View File

@ -17,7 +17,7 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
ViewSectionBloc({ ViewSectionBloc({
required AppViewDataContext appViewData, required AppViewDataContext appViewData,
}) : _appService = AppService(appId: appViewData.appId), }) : _appService = AppService(),
_appViewData = appViewData, _appViewData = appViewData,
super(ViewSectionState.initial(appViewData)) { super(ViewSectionState.initial(appViewData)) {
on<ViewSectionEvent>((event, emit) async { on<ViewSectionEvent>((event, emit) async {
@ -59,7 +59,8 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
} }
} }
Future<void> _moveView(_MoveView value, Emitter<ViewSectionState> emit) async { Future<void> _moveView(
_MoveView value, Emitter<ViewSectionState> emit) async {
if (value.fromIndex < state.views.length) { if (value.fromIndex < state.views.length) {
final viewId = state.views[value.fromIndex].id; final viewId = state.views[value.fromIndex].id;
final views = List<ViewPB>.from(state.views); final views = List<ViewPB>.from(state.views);
@ -92,9 +93,12 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
@freezed @freezed
class ViewSectionEvent with _$ViewSectionEvent { class ViewSectionEvent with _$ViewSectionEvent {
const factory ViewSectionEvent.initial() = _Initial; const factory ViewSectionEvent.initial() = _Initial;
const factory ViewSectionEvent.setSelectedView(ViewPB? view) = _SetSelectedView; const factory ViewSectionEvent.setSelectedView(ViewPB? view) =
const factory ViewSectionEvent.moveView(int fromIndex, int toIndex) = _MoveView; _SetSelectedView;
const factory ViewSectionEvent.didReceiveViewUpdated(List<ViewPB> views) = _DidReceiveViewUpdated; const factory ViewSectionEvent.moveView(int fromIndex, int toIndex) =
_MoveView;
const factory ViewSectionEvent.didReceiveViewUpdated(List<ViewPB> views) =
_DidReceiveViewUpdated;
} }
@freezed @freezed
@ -104,7 +108,8 @@ class ViewSectionState with _$ViewSectionState {
ViewPB? selectedView, ViewPB? selectedView,
}) = _ViewSectionState; }) = _ViewSectionState;
factory ViewSectionState.initial(AppViewDataContext appViewData) => ViewSectionState( factory ViewSectionState.initial(AppViewDataContext appViewData) =>
ViewSectionState(
views: appViewData.views, views: appViewData.views,
selectedView: appViewData.selectedView, selectedView: appViewData.selectedView,
); );

View File

@ -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_sdk/rust_stream.dart';
import 'package:flowy_infra/notifier.dart'; import 'package:flowy_infra/notifier.dart';
// Delete the view from trash, which means the view was deleted permanently
typedef DeleteViewNotifyValue = Either<ViewPB, FlowyError>; typedef DeleteViewNotifyValue = Either<ViewPB, FlowyError>;
// The view get updated
typedef UpdateViewNotifiedValue = Either<ViewPB, FlowyError>; typedef UpdateViewNotifiedValue = Either<ViewPB, FlowyError>;
// Restore the view from trash
typedef RestoreViewNotifiedValue = Either<ViewPB, FlowyError>; typedef RestoreViewNotifiedValue = Either<ViewPB, FlowyError>;
// Move the view to trash
typedef MoveToTrashNotifiedValue = Either<ViewIdPB, FlowyError>;
class ViewListener { class ViewListener {
StreamSubscription<SubscribeObject>? _subscription; StreamSubscription<SubscribeObject>? _subscription;
final PublishNotifier<UpdateViewNotifiedValue> _updatedViewNotifier = PublishNotifier(); final _updatedViewNotifier = PublishNotifier<UpdateViewNotifiedValue>();
final PublishNotifier<DeleteViewNotifyValue> _deletedNotifier = PublishNotifier(); final _deletedNotifier = PublishNotifier<DeleteViewNotifyValue>();
final PublishNotifier<RestoreViewNotifiedValue> _restoredNotifier = PublishNotifier(); final _restoredNotifier = PublishNotifier<RestoreViewNotifiedValue>();
final _moveToTrashNotifier = PublishNotifier<MoveToTrashNotifiedValue>();
FolderNotificationParser? _parser; FolderNotificationParser? _parser;
ViewPB view; ViewPB view;
@ -29,6 +35,7 @@ class ViewListener {
void Function(UpdateViewNotifiedValue)? onViewUpdated, void Function(UpdateViewNotifiedValue)? onViewUpdated,
void Function(DeleteViewNotifyValue)? onViewDeleted, void Function(DeleteViewNotifyValue)? onViewDeleted,
void Function(RestoreViewNotifiedValue)? onViewRestored, void Function(RestoreViewNotifiedValue)? onViewRestored,
void Function(MoveToTrashNotifiedValue)? onViewMoveToTrash,
}) { }) {
if (onViewUpdated != null) { if (onViewUpdated != null) {
_updatedViewNotifier.addListener(() { _updatedViewNotifier.addListener(() {
@ -48,6 +55,12 @@ class ViewListener {
}); });
} }
if (onViewMoveToTrash != null) {
_moveToTrashNotifier.addListener(() {
onViewMoveToTrash(_moveToTrashNotifier.currentValue!);
});
}
_parser = FolderNotificationParser( _parser = FolderNotificationParser(
id: view.id, id: view.id,
callback: (ty, result) { 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<Uint8List, FlowyError> result) { void _handleObservableType(
FolderNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) { switch (ty) {
case FolderNotification.ViewUpdated: case FolderNotification.ViewUpdated:
result.fold( result.fold(
(payload) => _updatedViewNotifier.value = left(ViewPB.fromBuffer(payload)), (payload) =>
_updatedViewNotifier.value = left(ViewPB.fromBuffer(payload)),
(error) => _updatedViewNotifier.value = right(error), (error) => _updatedViewNotifier.value = right(error),
); );
break; break;
case FolderNotification.ViewDeleted: case FolderNotification.ViewDeleted:
result.fold( result.fold(
(payload) => _deletedNotifier.value = left(ViewPB.fromBuffer(payload)), (payload) =>
_deletedNotifier.value = left(ViewPB.fromBuffer(payload)),
(error) => _deletedNotifier.value = right(error), (error) => _deletedNotifier.value = right(error),
); );
break; break;
case FolderNotification.ViewRestored: case FolderNotification.ViewRestored:
result.fold( result.fold(
(payload) => _restoredNotifier.value = left(ViewPB.fromBuffer(payload)), (payload) =>
_restoredNotifier.value = left(ViewPB.fromBuffer(payload)),
(error) => _restoredNotifier.value = right(error), (error) => _restoredNotifier.value = right(error),
); );
break; break;
case FolderNotification.ViewMoveToTrash:
result.fold(
(payload) =>
_moveToTrashNotifier.value = left(ViewIdPB.fromBuffer(payload)),
(error) => _moveToTrashNotifier.value = right(error),
);
break;
default: default:
break; break;
} }

View File

@ -5,7 +5,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.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/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:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart';
import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:app_flowy/generated/locale_keys.g.dart';
@ -15,7 +16,8 @@ class WorkspaceService {
WorkspaceService({ WorkspaceService({
required this.workspaceId, required this.workspaceId,
}); });
Future<Either<AppPB, FlowyError>> createApp({required String name, required String desc}) { Future<Either<AppPB, FlowyError>> createApp(
{required String name, required String desc}) {
final payload = CreateAppPayloadPB.create() final payload = CreateAppPayloadPB.create()
..name = name ..name = name
..workspaceId = workspaceId ..workspaceId = workspaceId
@ -31,7 +33,8 @@ class WorkspaceService {
assert(workspaces.items.length == 1); assert(workspaces.items.length == 1);
if (workspaces.items.isEmpty) { if (workspaces.items.isEmpty) {
return right(FlowyError.create()..msg = LocaleKeys.workspace_notFoundError.tr()); return right(FlowyError.create()
..msg = LocaleKeys.workspace_notFoundError.tr());
} else { } else {
return left(workspaces.items[0]); return left(workspaces.items[0]);
} }

View File

@ -1,5 +1,7 @@
import 'package:app_flowy/plugins/blank/blank.dart';
import 'package:app_flowy/startup/plugin/plugin.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_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/presentation/home/hotkeys.dart';
import 'package:app_flowy/workspace/application/view/view_ext.dart'; import 'package:app_flowy/workspace/application/view/view_ext.dart';
@ -78,7 +80,7 @@ class _HomeScreenState extends State<HomeScreen> {
return FlowyContainer( return FlowyContainer(
Theme.of(context).colorScheme.surface, Theme.of(context).colorScheme.surface,
// Colors.white, // Colors.white,
child: _buildBody(state), child: _buildBody(context, state),
); );
}, },
), ),
@ -87,12 +89,16 @@ class _HomeScreenState extends State<HomeScreen> {
); );
} }
Widget _buildBody(HomeState state) { Widget _buildBody(BuildContext context, HomeState state) {
return LayoutBuilder( return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) { builder: (BuildContext context, BoxConstraints constraints) {
final layout = HomeLayout(context, constraints, state.forceCollapse); final layout = HomeLayout(context, constraints, state.forceCollapse);
final homeStack = HomeStack( final homeStack = HomeStack(
layout: layout, layout: layout,
delegate: HomeScreenStackAdaptor(
buildContext: context,
homeState: state,
),
); );
final menu = _buildHomeMenu( final menu = _buildHomeMenu(
layout: layout, layout: layout,
@ -132,7 +138,7 @@ class _HomeScreenState extends State<HomeScreen> {
getIt<HomeStackManager>().setPlugin(plugin); getIt<HomeStackManager>().setPlugin(plugin);
} }
HomeMenu homeMenu = HomeMenu( final homeMenu = HomeMenu(
user: widget.user, user: widget.user,
workspaceSetting: workspaceSetting, workspaceSetting: workspaceSetting,
collapsedNotifier: getIt<HomeStackManager>().collapsedNotifier, collapsedNotifier: getIt<HomeStackManager>().collapsedNotifier,
@ -148,7 +154,6 @@ class _HomeScreenState extends State<HomeScreen> {
return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu)); return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu));
} }
Widget _buildEditPanel( Widget _buildEditPanel(
{required HomeState homeState, {required HomeState homeState,
required BuildContext context, required BuildContext context,
@ -245,3 +250,38 @@ class _HomeScreenState extends State<HomeScreen> {
); );
} }
} }
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<ViewPB> views = appPB.belongings.items;
if (views.isNotEmpty) {
final lastView = views.last;
final plugin = makePlugin(
pluginType: lastView.pluginType,
data: lastView,
);
getIt<MenuSharedState>().latestOpenView = lastView;
getIt<HomeStackManager>().setPlugin(plugin);
} else {
getIt<MenuSharedState>().latestOpenView = null;
getIt<HomeStackManager>().setPlugin(BlankPagePlugin());
}
},
(err) => Log.error(err),
);
});
}
}

View File

@ -2,7 +2,7 @@ import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/plugins/blank/blank.dart'; import 'package:app_flowy/plugins/blank/blank.dart';
import 'package:app_flowy/workspace/presentation/home/toast.dart'; import 'package:app_flowy/workspace/presentation/home/toast.dart';
import 'package:flowy_infra/theme.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:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:time/time.dart'; import 'package:time/time.dart';
@ -17,14 +17,21 @@ import 'home_layout.dart';
typedef NavigationCallback = void Function(String id); typedef NavigationCallback = void Function(String id);
class HomeStack extends StatelessWidget { abstract class HomeStackDelegate {
const HomeStack({Key? key, required this.layout}) : super(key: key); void didDeleteStackWidget(ViewPB view);
}
class HomeStack extends StatelessWidget {
final HomeStackDelegate delegate;
final HomeLayout layout; final HomeLayout layout;
const HomeStack({
required this.delegate,
required this.layout,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Log.info('HomePage build');
final theme = context.watch<AppTheme>(); final theme = context.watch<AppTheme>();
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
@ -34,7 +41,9 @@ class HomeStack extends StatelessWidget {
child: Container( child: Container(
color: theme.surface, color: theme.surface,
child: FocusTraversalGroup( child: FocusTraversalGroup(
child: getIt<HomeStackManager>().stackWidget(), child: getIt<HomeStackManager>().stackWidget(onDeleted: (view) {
delegate.didDeleteStackWidget(view);
}),
), ),
), ),
), ),
@ -114,18 +123,18 @@ class HomeStackNotifier extends ChangeNotifier {
return; return;
} }
_plugin.display.notifier?.removeListener(notifyListeners); _plugin.notifier?.isDisplayChanged.addListener(notifyListeners);
_plugin.dispose(); _plugin.dispose();
_plugin = newPlugin; _plugin = newPlugin;
_plugin.display.notifier?.addListener(notifyListeners); _plugin.notifier?.isDisplayChanged.removeListener(notifyListeners);
notifyListeners(); notifyListeners();
} }
Plugin get plugin => _plugin; 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 { class HomeStackManager {
final HomeStackNotifier _notifier = HomeStackNotifier(); final HomeStackNotifier _notifier = HomeStackNotifier();
HomeStackManager(); HomeStackManager();
@ -140,7 +149,9 @@ class HomeStackManager {
_notifier.plugin = newPlugin; _notifier.plugin = newPlugin;
} }
void setStackWithId(String id) {} void setStackWithId(String id) {
// Navigate to the page with id
}
Widget stackTopBar({required HomeLayout layout}) { Widget stackTopBar({required HomeLayout layout}) {
return MultiProvider( return MultiProvider(
@ -156,18 +167,16 @@ class HomeStackManager {
); );
} }
Widget stackWidget() { Widget stackWidget({required Function(ViewPB) onDeleted}) {
return MultiProvider( return MultiProvider(
providers: [ providers: [ChangeNotifierProvider.value(value: _notifier)],
ChangeNotifierProvider.value(value: _notifier),
],
child: Consumer(builder: (ctx, HomeStackNotifier notifier, child) { child: Consumer(builder: (ctx, HomeStackNotifier notifier, child) {
return FadingIndexedStack( return FadingIndexedStack(
index: getIt<PluginSandbox>().indexOf(notifier.plugin.ty), index: getIt<PluginSandbox>().indexOf(notifier.plugin.ty),
children: getIt<PluginSandbox>().supportPluginTypes.map((pluginType) { children: getIt<PluginSandbox>().supportPluginTypes.map((pluginType) {
if (pluginType == notifier.plugin.ty) { if (pluginType == notifier.plugin.ty) {
return notifier.plugin.display return notifier.plugin.display
.buildWidget() .buildWidget(PluginContext(onDeleted: onDeleted))
.padding(horizontal: 40, vertical: 28); .padding(horizontal: 40, vertical: 28);
} else { } else {
return const BlankPage(); return const BlankPage();

View File

@ -42,7 +42,12 @@ class _MenuAppState extends State<MenuApp> {
listeners: [ listeners: [
BlocListener<AppBloc, AppState>( BlocListener<AppBloc, AppState>(
listenWhen: (p, c) => p.latestCreatedView != c.latestCreatedView, listenWhen: (p, c) => p.latestCreatedView != c.latestCreatedView,
listener: (context, state) => getIt<MenuSharedState>().latestOpenView = state.latestCreatedView, listener: (context, state) {
if (state.latestCreatedView != null) {
getIt<MenuSharedState>().latestOpenView =
state.latestCreatedView;
}
},
), ),
BlocListener<AppBloc, AppState>( BlocListener<AppBloc, AppState>(
listenWhen: (p, c) => p.views != c.views, listenWhen: (p, c) => p.views != c.views,
@ -65,7 +70,8 @@ class _MenuAppState extends State<MenuApp> {
); );
} }
ExpandableNotifier expandableWrapper(BuildContext context, AppViewDataContext viewDataContext) { ExpandableNotifier expandableWrapper(
BuildContext context, AppViewDataContext viewDataContext) {
return ExpandableNotifier( return ExpandableNotifier(
controller: viewDataContext.expandController, controller: viewDataContext.expandController,
child: ScrollOnExpand( child: ScrollOnExpand(
@ -83,7 +89,8 @@ class _MenuAppState extends State<MenuApp> {
hasIcon: false, hasIcon: false,
), ),
header: ChangeNotifierProvider.value( header: ChangeNotifierProvider.value(
value: Provider.of<AppearanceSettingModel>(context, listen: true), value:
Provider.of<AppearanceSettingModel>(context, listen: true),
child: MenuAppHeader(widget.app), child: MenuAppHeader(widget.app),
), ),
expanded: ViewSection(appViewData: viewDataContext), expanded: ViewSection(appViewData: viewDataContext),

View File

@ -16,6 +16,7 @@ pub(crate) enum FolderNotification {
ViewUpdated = 31, ViewUpdated = 31,
ViewDeleted = 32, ViewDeleted = 32,
ViewRestored = 33, ViewRestored = 33,
ViewMoveToTrash = 34,
UserUnauthorized = 100, UserUnauthorized = 100,
TrashUpdated = 1000, TrashUpdated = 1000,
} }

View File

@ -251,8 +251,6 @@ pub trait ViewDataProcessor {
fn create_container(&self, user_id: &str, view_id: &str, delta_data: Bytes) -> FutureResult<(), FlowyError>; 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 close_container(&self, view_id: &str) -> FutureResult<(), FlowyError>;
fn get_delta_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError>; fn get_delta_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError>;

View File

@ -125,7 +125,7 @@ impl ViewController {
} }
#[tracing::instrument(level = "debug", skip(self, view_id), fields(view_id = %view_id.value), err)] #[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<ViewInfoPB, FlowyError> { pub(crate) async fn read_view_pb(&self, view_id: ViewIdPB) -> Result<ViewInfoPB, FlowyError> {
let view_info = self let view_info = self
.persistence .persistence
.begin_transaction(|transaction| { .begin_transaction(|transaction| {
@ -179,14 +179,20 @@ impl ViewController {
} }
#[tracing::instrument(level = "debug", skip(self,params), fields(doc_id = %params.value), err)] #[tracing::instrument(level = "debug", skip(self,params), fields(doc_id = %params.value), err)]
pub(crate) async fn delete_view(&self, params: TextBlockIdPB) -> Result<(), FlowyError> { pub(crate) async fn move_view_to_trash(&self, params: TextBlockIdPB) -> Result<(), FlowyError> {
if let Some(view_id) = KV::get_str(LATEST_VIEW_ID) { let view_id = params.value;
if 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 _ = KV::remove(LATEST_VIEW_ID);
} }
} }
let processor = self.get_data_processor_from_view_id(&params.value).await?; let view_id_pb = ViewIdPB::from(view_id.as_str());
let _ = processor.delete_container(&params.value).await?; 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(()) Ok(())
} }

View File

@ -40,7 +40,7 @@ pub(crate) async fn read_view_info_handler(
controller: AppData<Arc<ViewController>>, controller: AppData<Arc<ViewController>>,
) -> DataResult<ViewInfoPB, FlowyError> { ) -> DataResult<ViewInfoPB, FlowyError> {
let view_id: ViewIdPB = data.into_inner(); 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) data_result(view_info)
} }
@ -62,7 +62,7 @@ pub(crate) async fn delete_view_handler(
) -> Result<(), FlowyError> { ) -> Result<(), FlowyError> {
let params: RepeatedViewIdPB = data.into_inner(); let params: RepeatedViewIdPB = data.into_inner();
for view_id in &params.items { for view_id in &params.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 let trash = view_controller

View File

@ -109,17 +109,6 @@ impl GridManager {
Ok(()) Ok(())
} }
#[tracing::instrument(level = "debug", skip(self, grid_id), fields(doc_id), err)]
pub async fn delete_grid<T: AsRef<str>>(&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)] // #[tracing::instrument(level = "debug", skip(self), err)]
pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridRevisionEditor>> { pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridRevisionEditor>> {
match self.grid_editors.get(grid_id) { match self.grid_editors.get(grid_id) {

View File

@ -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> { fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError> {
let manager = self.0.clone(); let manager = self.0.clone();
let view_id = view_id.to_string(); 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> { fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError> {
let grid_manager = self.0.clone(); let grid_manager = self.0.clone();
let view_id = view_id.to_string(); let view_id = view_id.to_string();