mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: open next page when current page get deleted
This commit is contained in:
parent
bdf4e60b48
commit
37f85cebde
@ -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<NavigationItem> get navigationItems => [this];
|
||||
|
@ -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<NavigationItem> get navigationItems => [this];
|
||||
|
@ -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) {
|
||||
|
@ -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<int> {
|
||||
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<ViewListener>(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<int> 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<int> with NavigationItem {
|
||||
final PublishNotifier<int> _displayNotifier = PublishNotifier<int>();
|
||||
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<NavigationItem> get navigationItems => [this];
|
||||
|
||||
@override
|
||||
PublishNotifier<int>? get notifier => _displayNotifier;
|
||||
}
|
||||
|
||||
class DocumentShareButton extends StatelessWidget {
|
||||
|
@ -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<DocumentPage> createState() => _DocumentPageState();
|
||||
@ -49,7 +54,8 @@ class _DocumentPageState extends State<DocumentPage> {
|
||||
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<DocumentPage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderAppPage() {
|
||||
return Container(
|
||||
color: Colors.black,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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<NavigationItem> get navigationItems => [this];
|
||||
|
@ -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<GridPage> createState() => _GridPageState();
|
||||
|
@ -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<NavigationItem> get navigationItems => [this];
|
||||
|
44
frontend/app_flowy/lib/plugins/util.dart
Normal file
44
frontend/app_flowy/lib/plugins/util.dart
Normal 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();
|
||||
}
|
||||
}
|
@ -120,7 +120,7 @@ void _resolveFolderDeps(GetIt getIt) {
|
||||
getIt.registerFactoryParam<AppBloc, AppPB, void>(
|
||||
(app, _) => AppBloc(
|
||||
app: app,
|
||||
appService: AppService(appId: app.id),
|
||||
appService: AppService(),
|
||||
appListener: AppListener(appId: app.id),
|
||||
),
|
||||
);
|
||||
|
@ -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<T> {
|
||||
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<bool> get isDeleted;
|
||||
|
||||
/// Notify if the [PluginDisplay]'s content was changed
|
||||
ValueNotifier<int> get isDisplayChanged;
|
||||
|
||||
void dispose() {}
|
||||
}
|
||||
|
||||
@ -64,12 +59,17 @@ abstract class PluginConfig {
|
||||
bool get creatable => true;
|
||||
}
|
||||
|
||||
abstract class PluginDisplay<T> with NavigationItem {
|
||||
abstract class PluginDisplay with NavigationItem {
|
||||
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}) {
|
||||
|
@ -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<Either<AppPB, FlowyError>> getAppDesc({required String appId}) {
|
||||
Future<Either<AppPB, FlowyError>> readApp({required String appId}) {
|
||||
final payload = AppIdPB.create()..value = appId;
|
||||
|
||||
return FolderEventReadApp(payload).send();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
|
||||
|
||||
ViewSectionBloc({
|
||||
required AppViewDataContext appViewData,
|
||||
}) : _appService = AppService(appId: appViewData.appId),
|
||||
}) : _appService = AppService(),
|
||||
_appViewData = appViewData,
|
||||
super(ViewSectionState.initial(appViewData)) {
|
||||
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) {
|
||||
final viewId = state.views[value.fromIndex].id;
|
||||
final views = List<ViewPB>.from(state.views);
|
||||
@ -92,9 +93,12 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
|
||||
@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<ViewPB> views) = _DidReceiveViewUpdated;
|
||||
const factory ViewSectionEvent.setSelectedView(ViewPB? view) =
|
||||
_SetSelectedView;
|
||||
const factory ViewSectionEvent.moveView(int fromIndex, int toIndex) =
|
||||
_MoveView;
|
||||
const factory ViewSectionEvent.didReceiveViewUpdated(List<ViewPB> 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,
|
||||
);
|
||||
|
@ -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<ViewPB, FlowyError>;
|
||||
// The view get updated
|
||||
typedef UpdateViewNotifiedValue = Either<ViewPB, FlowyError>;
|
||||
// Restore the view from trash
|
||||
typedef RestoreViewNotifiedValue = Either<ViewPB, FlowyError>;
|
||||
// Move the view to trash
|
||||
typedef MoveToTrashNotifiedValue = Either<ViewIdPB, FlowyError>;
|
||||
|
||||
class ViewListener {
|
||||
StreamSubscription<SubscribeObject>? _subscription;
|
||||
final PublishNotifier<UpdateViewNotifiedValue> _updatedViewNotifier = PublishNotifier();
|
||||
final PublishNotifier<DeleteViewNotifyValue> _deletedNotifier = PublishNotifier();
|
||||
final PublishNotifier<RestoreViewNotifiedValue> _restoredNotifier = PublishNotifier();
|
||||
final _updatedViewNotifier = PublishNotifier<UpdateViewNotifiedValue>();
|
||||
final _deletedNotifier = PublishNotifier<DeleteViewNotifyValue>();
|
||||
final _restoredNotifier = PublishNotifier<RestoreViewNotifiedValue>();
|
||||
final _moveToTrashNotifier = PublishNotifier<MoveToTrashNotifiedValue>();
|
||||
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<Uint8List, FlowyError> result) {
|
||||
void _handleObservableType(
|
||||
FolderNotification ty, Either<Uint8List, FlowyError> 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;
|
||||
}
|
||||
|
@ -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<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()
|
||||
..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]);
|
||||
}
|
||||
|
@ -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<HomeScreen> {
|
||||
return FlowyContainer(
|
||||
Theme.of(context).colorScheme.surface,
|
||||
// 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(
|
||||
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<HomeScreen> {
|
||||
getIt<HomeStackManager>().setPlugin(plugin);
|
||||
}
|
||||
|
||||
HomeMenu homeMenu = HomeMenu(
|
||||
final homeMenu = HomeMenu(
|
||||
user: widget.user,
|
||||
workspaceSetting: workspaceSetting,
|
||||
collapsedNotifier: getIt<HomeStackManager>().collapsedNotifier,
|
||||
@ -148,7 +154,6 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu));
|
||||
}
|
||||
|
||||
|
||||
Widget _buildEditPanel(
|
||||
{required HomeState homeState,
|
||||
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),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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<AppTheme>();
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
@ -34,7 +41,9 @@ class HomeStack extends StatelessWidget {
|
||||
child: Container(
|
||||
color: theme.surface,
|
||||
child: FocusTraversalGroup(
|
||||
child: getIt<HomeStackManager>().stackWidget(),
|
||||
child: getIt<HomeStackManager>().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<PluginSandbox>().indexOf(notifier.plugin.ty),
|
||||
children: getIt<PluginSandbox>().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();
|
||||
|
@ -42,7 +42,12 @@ class _MenuAppState extends State<MenuApp> {
|
||||
listeners: [
|
||||
BlocListener<AppBloc, AppState>(
|
||||
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>(
|
||||
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(
|
||||
controller: viewDataContext.expandController,
|
||||
child: ScrollOnExpand(
|
||||
@ -83,7 +89,8 @@ class _MenuAppState extends State<MenuApp> {
|
||||
hasIcon: false,
|
||||
),
|
||||
header: ChangeNotifierProvider.value(
|
||||
value: Provider.of<AppearanceSettingModel>(context, listen: true),
|
||||
value:
|
||||
Provider.of<AppearanceSettingModel>(context, listen: true),
|
||||
child: MenuAppHeader(widget.app),
|
||||
),
|
||||
expanded: ViewSection(appViewData: viewDataContext),
|
||||
|
@ -16,6 +16,7 @@ pub(crate) enum FolderNotification {
|
||||
ViewUpdated = 31,
|
||||
ViewDeleted = 32,
|
||||
ViewRestored = 33,
|
||||
ViewMoveToTrash = 34,
|
||||
UserUnauthorized = 100,
|
||||
TrashUpdated = 1000,
|
||||
}
|
||||
|
@ -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<Bytes, FlowyError>;
|
||||
|
@ -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<ViewInfoPB, FlowyError> {
|
||||
pub(crate) async fn read_view_pb(&self, view_id: ViewIdPB) -> Result<ViewInfoPB, FlowyError> {
|
||||
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(())
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ pub(crate) async fn read_view_info_handler(
|
||||
controller: AppData<Arc<ViewController>>,
|
||||
) -> DataResult<ViewInfoPB, FlowyError> {
|
||||
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
|
||||
|
@ -109,17 +109,6 @@ impl GridManager {
|
||||
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)]
|
||||
pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridRevisionEditor>> {
|
||||
match self.grid_editors.get(grid_id) {
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user