diff --git a/app_flowy/lib/workspace/application/doc/doc_bloc.dart b/app_flowy/lib/workspace/application/doc/doc_bloc.dart index 4ec072a938..0cddc5a9d0 100644 --- a/app_flowy/lib/workspace/application/doc/doc_bloc.dart +++ b/app_flowy/lib/workspace/application/doc/doc_bloc.dart @@ -1,6 +1,8 @@ import 'dart:convert'; +import 'package:app_flowy/workspace/domain/i_trash.dart'; import 'package:app_flowy/workspace/domain/i_view.dart'; +import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:flowy_log/flowy_log.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart'; @@ -12,12 +14,19 @@ import 'dart:async'; part 'doc_bloc.freezed.dart'; class DocBloc extends Bloc { + final View view; final IDoc docManager; final IViewListener listener; + final ITrash trasnManager; late Document document; late StreamSubscription _subscription; - DocBloc({required this.docManager, required this.listener}) : super(DocState.initial()); + DocBloc({ + required this.view, + required this.docManager, + required this.listener, + required this.trasnManager, + }) : super(DocState.initial()); @override Stream mapEventToState(DocEvent event) async* { @@ -29,6 +38,17 @@ class DocBloc extends Bloc { restore: (Restore value) async* { yield state.copyWith(isDeleted: false); }, + deletePermanently: (DeletePermanently value) async* { + // final result = await trasnManager.deleteViews([e.trash]); + // yield* _handleResult(result); + yield state; + }, + restorePage: (RestorePage value) async* { + final result = await trasnManager.putback(view.id); + yield result.fold((l) => state.copyWith(isDeleted: false), (r) { + return state; + }); + }, ); } @@ -107,6 +127,8 @@ class DocEvent with _$DocEvent { const factory DocEvent.initial() = Initial; const factory DocEvent.deleted() = Deleted; const factory DocEvent.restore() = Restore; + const factory DocEvent.restorePage() = RestorePage; + const factory DocEvent.deletePermanently() = DeletePermanently; } @freezed diff --git a/app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart b/app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart index 754ff95530..1614ba8efd 100644 --- a/app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart +++ b/app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart @@ -27,6 +27,14 @@ class _$DocEventTearOff { Restore restore() { return const Restore(); } + + RestorePage restorePage() { + return const RestorePage(); + } + + DeletePermanently deletePermanently() { + return const DeletePermanently(); + } } /// @nodoc @@ -39,6 +47,8 @@ mixin _$DocEvent { required TResult Function() initial, required TResult Function() deleted, required TResult Function() restore, + required TResult Function() restorePage, + required TResult Function() deletePermanently, }) => throw _privateConstructorUsedError; @optionalTypeArgs @@ -46,6 +56,8 @@ mixin _$DocEvent { TResult Function()? initial, TResult Function()? deleted, TResult Function()? restore, + TResult Function()? restorePage, + TResult Function()? deletePermanently, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -54,6 +66,8 @@ mixin _$DocEvent { required TResult Function(Initial value) initial, required TResult Function(Deleted value) deleted, required TResult Function(Restore value) restore, + required TResult Function(RestorePage value) restorePage, + required TResult Function(DeletePermanently value) deletePermanently, }) => throw _privateConstructorUsedError; @optionalTypeArgs @@ -61,6 +75,8 @@ mixin _$DocEvent { TResult Function(Initial value)? initial, TResult Function(Deleted value)? deleted, TResult Function(Restore value)? restore, + TResult Function(RestorePage value)? restorePage, + TResult Function(DeletePermanently value)? deletePermanently, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -121,6 +137,8 @@ class _$Initial implements Initial { required TResult Function() initial, required TResult Function() deleted, required TResult Function() restore, + required TResult Function() restorePage, + required TResult Function() deletePermanently, }) { return initial(); } @@ -131,6 +149,8 @@ class _$Initial implements Initial { TResult Function()? initial, TResult Function()? deleted, TResult Function()? restore, + TResult Function()? restorePage, + TResult Function()? deletePermanently, required TResult orElse(), }) { if (initial != null) { @@ -145,6 +165,8 @@ class _$Initial implements Initial { required TResult Function(Initial value) initial, required TResult Function(Deleted value) deleted, required TResult Function(Restore value) restore, + required TResult Function(RestorePage value) restorePage, + required TResult Function(DeletePermanently value) deletePermanently, }) { return initial(this); } @@ -155,6 +177,8 @@ class _$Initial implements Initial { TResult Function(Initial value)? initial, TResult Function(Deleted value)? deleted, TResult Function(Restore value)? restore, + TResult Function(RestorePage value)? restorePage, + TResult Function(DeletePermanently value)? deletePermanently, required TResult orElse(), }) { if (initial != null) { @@ -208,6 +232,8 @@ class _$Deleted implements Deleted { required TResult Function() initial, required TResult Function() deleted, required TResult Function() restore, + required TResult Function() restorePage, + required TResult Function() deletePermanently, }) { return deleted(); } @@ -218,6 +244,8 @@ class _$Deleted implements Deleted { TResult Function()? initial, TResult Function()? deleted, TResult Function()? restore, + TResult Function()? restorePage, + TResult Function()? deletePermanently, required TResult orElse(), }) { if (deleted != null) { @@ -232,6 +260,8 @@ class _$Deleted implements Deleted { required TResult Function(Initial value) initial, required TResult Function(Deleted value) deleted, required TResult Function(Restore value) restore, + required TResult Function(RestorePage value) restorePage, + required TResult Function(DeletePermanently value) deletePermanently, }) { return deleted(this); } @@ -242,6 +272,8 @@ class _$Deleted implements Deleted { TResult Function(Initial value)? initial, TResult Function(Deleted value)? deleted, TResult Function(Restore value)? restore, + TResult Function(RestorePage value)? restorePage, + TResult Function(DeletePermanently value)? deletePermanently, required TResult orElse(), }) { if (deleted != null) { @@ -295,6 +327,8 @@ class _$Restore implements Restore { required TResult Function() initial, required TResult Function() deleted, required TResult Function() restore, + required TResult Function() restorePage, + required TResult Function() deletePermanently, }) { return restore(); } @@ -305,6 +339,8 @@ class _$Restore implements Restore { TResult Function()? initial, TResult Function()? deleted, TResult Function()? restore, + TResult Function()? restorePage, + TResult Function()? deletePermanently, required TResult orElse(), }) { if (restore != null) { @@ -319,6 +355,8 @@ class _$Restore implements Restore { required TResult Function(Initial value) initial, required TResult Function(Deleted value) deleted, required TResult Function(Restore value) restore, + required TResult Function(RestorePage value) restorePage, + required TResult Function(DeletePermanently value) deletePermanently, }) { return restore(this); } @@ -329,6 +367,8 @@ class _$Restore implements Restore { TResult Function(Initial value)? initial, TResult Function(Deleted value)? deleted, TResult Function(Restore value)? restore, + TResult Function(RestorePage value)? restorePage, + TResult Function(DeletePermanently value)? deletePermanently, required TResult orElse(), }) { if (restore != null) { @@ -342,6 +382,200 @@ abstract class Restore implements DocEvent { const factory Restore() = _$Restore; } +/// @nodoc +abstract class $RestorePageCopyWith<$Res> { + factory $RestorePageCopyWith( + RestorePage value, $Res Function(RestorePage) then) = + _$RestorePageCopyWithImpl<$Res>; +} + +/// @nodoc +class _$RestorePageCopyWithImpl<$Res> extends _$DocEventCopyWithImpl<$Res> + implements $RestorePageCopyWith<$Res> { + _$RestorePageCopyWithImpl( + RestorePage _value, $Res Function(RestorePage) _then) + : super(_value, (v) => _then(v as RestorePage)); + + @override + RestorePage get _value => super._value as RestorePage; +} + +/// @nodoc + +class _$RestorePage implements RestorePage { + const _$RestorePage(); + + @override + String toString() { + return 'DocEvent.restorePage()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || (other is RestorePage); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() deleted, + required TResult Function() restore, + required TResult Function() restorePage, + required TResult Function() deletePermanently, + }) { + return restorePage(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? deleted, + TResult Function()? restore, + TResult Function()? restorePage, + TResult Function()? deletePermanently, + required TResult orElse(), + }) { + if (restorePage != null) { + return restorePage(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(Initial value) initial, + required TResult Function(Deleted value) deleted, + required TResult Function(Restore value) restore, + required TResult Function(RestorePage value) restorePage, + required TResult Function(DeletePermanently value) deletePermanently, + }) { + return restorePage(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(Initial value)? initial, + TResult Function(Deleted value)? deleted, + TResult Function(Restore value)? restore, + TResult Function(RestorePage value)? restorePage, + TResult Function(DeletePermanently value)? deletePermanently, + required TResult orElse(), + }) { + if (restorePage != null) { + return restorePage(this); + } + return orElse(); + } +} + +abstract class RestorePage implements DocEvent { + const factory RestorePage() = _$RestorePage; +} + +/// @nodoc +abstract class $DeletePermanentlyCopyWith<$Res> { + factory $DeletePermanentlyCopyWith( + DeletePermanently value, $Res Function(DeletePermanently) then) = + _$DeletePermanentlyCopyWithImpl<$Res>; +} + +/// @nodoc +class _$DeletePermanentlyCopyWithImpl<$Res> extends _$DocEventCopyWithImpl<$Res> + implements $DeletePermanentlyCopyWith<$Res> { + _$DeletePermanentlyCopyWithImpl( + DeletePermanently _value, $Res Function(DeletePermanently) _then) + : super(_value, (v) => _then(v as DeletePermanently)); + + @override + DeletePermanently get _value => super._value as DeletePermanently; +} + +/// @nodoc + +class _$DeletePermanently implements DeletePermanently { + const _$DeletePermanently(); + + @override + String toString() { + return 'DocEvent.deletePermanently()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || (other is DeletePermanently); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() deleted, + required TResult Function() restore, + required TResult Function() restorePage, + required TResult Function() deletePermanently, + }) { + return deletePermanently(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? deleted, + TResult Function()? restore, + TResult Function()? restorePage, + TResult Function()? deletePermanently, + required TResult orElse(), + }) { + if (deletePermanently != null) { + return deletePermanently(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(Initial value) initial, + required TResult Function(Deleted value) deleted, + required TResult Function(Restore value) restore, + required TResult Function(RestorePage value) restorePage, + required TResult Function(DeletePermanently value) deletePermanently, + }) { + return deletePermanently(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(Initial value)? initial, + TResult Function(Deleted value)? deleted, + TResult Function(Restore value)? restore, + TResult Function(RestorePage value)? restorePage, + TResult Function(DeletePermanently value)? deletePermanently, + required TResult orElse(), + }) { + if (deletePermanently != null) { + return deletePermanently(this); + } + return orElse(); + } +} + +abstract class DeletePermanently implements DocEvent { + const factory DeletePermanently() = _$DeletePermanently; +} + /// @nodoc class _$DocStateTearOff { const _$DocStateTearOff(); diff --git a/app_flowy/lib/workspace/infrastructure/deps_resolver.dart b/app_flowy/lib/workspace/infrastructure/deps_resolver.dart index ebc04ca788..5e74b9be7b 100644 --- a/app_flowy/lib/workspace/infrastructure/deps_resolver.dart +++ b/app_flowy/lib/workspace/infrastructure/deps_resolver.dart @@ -89,8 +89,10 @@ class HomeDepsResolver { // Doc getIt.registerFactoryParam( (view, _) => DocBloc( + view: view, docManager: getIt(param1: view.id), listener: getIt(param1: view), + trasnManager: getIt(), ), ); diff --git a/app_flowy/lib/workspace/presentation/stack_page/doc/doc_page.dart b/app_flowy/lib/workspace/presentation/stack_page/doc/doc_page.dart index f381d9bda6..c9998413ad 100644 --- a/app_flowy/lib/workspace/presentation/stack_page/doc/doc_page.dart +++ b/app_flowy/lib/workspace/presentation/stack_page/doc/doc_page.dart @@ -2,7 +2,7 @@ import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/doc/doc_bloc.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; -import 'package:flutter_quill/flutter_quill.dart'; +import 'package:flutter_quill/flutter_quill.dart' as quill; import 'package:flowy_infra_ui/style_widget/progress_indicator.dart'; import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart'; @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:styled_widget/styled_widget.dart'; import 'styles.dart'; +import 'widget/banner.dart'; import 'widget/toolbar/tool_bar.dart'; class DocPage extends StatefulWidget { @@ -42,7 +43,7 @@ class _DocPageState extends State { return state.loadState.map( loading: (_) => const FlowyProgressIndicator(), finish: (result) => result.successOrFail.fold( - (_) => _renderDoc(context), + (_) => _renderDoc(context, state), (err) => FlowyErrorPage(err.toString()), ), ); @@ -56,24 +57,42 @@ class _DocPageState extends State { super.dispose(); } - Widget _renderDoc(BuildContext context) { - QuillController controller = QuillController( + Widget _renderDoc(BuildContext context, DocState state) { + quill.QuillController controller = quill.QuillController( document: context.read().document, selection: const TextSelection.collapsed(offset: 0), ); return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - _renderEditor(controller), - const VSpace(10), - _renderToolbar(controller), - const VSpace(10), + if (state.isDeleted) _renderBanner(context), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _renderEditor(controller), + const VSpace(10), + _renderToolbar(controller), + const VSpace(10), + ], + ).padding(horizontal: 40, top: 28), + ), ], - ).padding(horizontal: 40, top: 28); + ); } - Widget _renderEditor(QuillController controller) { - final editor = QuillEditor( + Widget _renderBanner(BuildContext context) { + return DocBanner( + onRestore: () { + context.read().add(const DocEvent.restorePage()); + }, + onDelete: () { + context.read().add(const DocEvent.deletePermanently()); + }, + ); + } + + Widget _renderEditor(quill.QuillController controller) { + final editor = quill.QuillEditor( controller: controller, focusNode: _focusNode, scrollable: true, @@ -96,7 +115,7 @@ class _DocPageState extends State { ); } - Widget _renderToolbar(QuillController controller) { + Widget _renderToolbar(quill.QuillController controller) { return EditorToolbar.basic( controller: controller, ); diff --git a/app_flowy/lib/workspace/presentation/stack_page/doc/widget/banner.dart b/app_flowy/lib/workspace/presentation/stack_page/doc/widget/banner.dart new file mode 100644 index 0000000000..26d9e332cf --- /dev/null +++ b/app_flowy/lib/workspace/presentation/stack_page/doc/widget/banner.dart @@ -0,0 +1,55 @@ +import 'package:flowy_infra/size.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flowy_infra_ui/widget/buttons/base_styled_button.dart'; +import 'package:flowy_infra_ui/widget/spacing.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class DocBanner extends StatelessWidget { + final void Function() onRestore; + final void Function() onDelete; + const DocBanner({required this.onRestore, required this.onDelete, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + // [[Row]] CrossAxisAlignment vs mainAxisAlignment + // https://stackoverflow.com/questions/53850149/flutter-crossaxisalignment-vs-mainaxisalignment + return Container( + color: theme.main1, + height: 60, + child: Row( + children: [ + const FlowyText.medium('This page is in Trash', color: Colors.white), + const HSpace(20), + BaseStyledButton( + minWidth: 160, + minHeight: 40, + contentPadding: EdgeInsets.zero, + bgColor: Colors.transparent, + hoverColor: theme.main2, + downColor: theme.main1, + outlineColor: Colors.white, + borderRadius: Corners.s8Border, + child: const FlowyText.medium('Restore page', color: Colors.white, fontSize: 14), + onPressed: onRestore), + const HSpace(20), + BaseStyledButton( + minWidth: 220, + minHeight: 40, + contentPadding: EdgeInsets.zero, + bgColor: Colors.transparent, + hoverColor: theme.main2, + downColor: theme.main1, + outlineColor: Colors.white, + borderRadius: Corners.s8Border, + child: const FlowyText.medium('Delete permanently', color: Colors.white, fontSize: 14), + onPressed: onDelete), + ], + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + ), + ); + } +}