mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-25 07:07:32 -04:00
* feat: add callout plugin * refactor: add SelectionMenuItem.node factory makes calloutMenuItem more readable * feat: add color picker * feat: add popover to callout * feat: add emoji to callout * fix: store tint name * fix: remove leading underscores * fix: revert export of editor_entry * refactor: move color tint names to appflowy_editor * fix: #1732 only re-insert text node if it's parent is text node too while deleting * docs: doc comment for SelectionMenuItem.node * fix: disable callout plugin should be re-enabled after #1753 is done * fix: typo --------- Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
149 lines
4.1 KiB
Dart
149 lines
4.1 KiB
Dart
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
|
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
|
|
import 'package:flowy_infra_ui/widget/error_page.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:intl/intl.dart';
|
|
|
|
import '../../startup/startup.dart';
|
|
import 'application/doc_bloc.dart';
|
|
import 'editor_styles.dart';
|
|
import 'presentation/banner.dart';
|
|
|
|
class DocumentPage extends StatefulWidget {
|
|
final VoidCallback onDeleted;
|
|
final ViewPB view;
|
|
|
|
DocumentPage({
|
|
required this.view,
|
|
required this.onDeleted,
|
|
Key? key,
|
|
}) : super(key: ValueKey(view.id));
|
|
|
|
@override
|
|
State<DocumentPage> createState() => _DocumentPageState();
|
|
}
|
|
|
|
class _DocumentPageState extends State<DocumentPage> {
|
|
late DocumentBloc documentBloc;
|
|
final FocusNode _focusNode = FocusNode();
|
|
|
|
@override
|
|
void initState() {
|
|
// The appflowy editor use Intl as localization, set the default language as fallback.
|
|
Intl.defaultLocale = 'en_US';
|
|
documentBloc = getIt<DocumentBloc>(param1: super.widget.view)
|
|
..add(const DocumentEvent.initial());
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MultiBlocProvider(
|
|
providers: [
|
|
BlocProvider<DocumentBloc>.value(value: documentBloc),
|
|
],
|
|
child:
|
|
BlocBuilder<DocumentBloc, DocumentState>(builder: (context, state) {
|
|
return state.loadingState.map(
|
|
loading: (_) => SizedBox.expand(
|
|
child: Container(color: Colors.transparent),
|
|
),
|
|
finish: (result) => result.successOrFail.fold(
|
|
(_) {
|
|
if (state.forceClose) {
|
|
widget.onDeleted();
|
|
return const SizedBox();
|
|
} else {
|
|
return _renderDocument(context, state);
|
|
}
|
|
},
|
|
(err) => FlowyErrorPage(err.toString()),
|
|
),
|
|
);
|
|
}),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> dispose() async {
|
|
documentBloc.close();
|
|
_focusNode.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Widget _renderDocument(BuildContext context, DocumentState state) {
|
|
return Column(
|
|
children: [
|
|
if (state.isDeleted) _renderBanner(context),
|
|
// AppFlowy Editor
|
|
_renderAppFlowyEditor(
|
|
context.read<DocumentBloc>().editorState,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _renderBanner(BuildContext context) {
|
|
return DocumentBanner(
|
|
onRestore: () =>
|
|
context.read<DocumentBloc>().add(const DocumentEvent.restorePage()),
|
|
onDelete: () => context
|
|
.read<DocumentBloc>()
|
|
.add(const DocumentEvent.deletePermanently()),
|
|
);
|
|
}
|
|
|
|
Widget _renderAppFlowyEditor(EditorState editorState) {
|
|
final theme = Theme.of(context);
|
|
final editorMaxWidth = MediaQuery.of(context).size.width * 0.6;
|
|
final editor = AppFlowyEditor(
|
|
editorState: editorState,
|
|
autoFocus: editorState.document.isEmpty,
|
|
customBuilders: {
|
|
// Divider
|
|
kDividerType: DividerWidgetBuilder(),
|
|
// Math Equation
|
|
kMathEquationType: MathEquationNodeWidgetBuidler(),
|
|
// Code Block
|
|
kCodeBlockType: CodeBlockNodeWidgetBuilder(),
|
|
// Card
|
|
kCalloutType: CalloutNodeWidgetBuilder(),
|
|
},
|
|
shortcutEvents: [
|
|
// Divider
|
|
insertDividerEvent,
|
|
// Code Block
|
|
enterInCodeBlock,
|
|
ignoreKeysInCodeBlock,
|
|
pasteInCodeBlock,
|
|
],
|
|
selectionMenuItems: [
|
|
// Divider
|
|
dividerMenuItem,
|
|
// Math Equation
|
|
mathEquationMenuItem,
|
|
// Code Block
|
|
codeBlockMenuItem,
|
|
// Emoji
|
|
emojiMenuItem,
|
|
],
|
|
themeData: theme.copyWith(extensions: [
|
|
...theme.extensions.values,
|
|
customEditorTheme(context),
|
|
...customPluginTheme(context),
|
|
]),
|
|
);
|
|
return Expanded(
|
|
child: Center(
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: editorMaxWidth,
|
|
),
|
|
child: editor,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|