document_page.dart 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import 'package:app_flowy/plugins/document/presentation/plugins/board/board_menu_item.dart';
  2. import 'package:app_flowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
  3. import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_menu_item.dart';
  4. import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
  5. import 'package:app_flowy/plugins/document/presentation/plugins/openai/widgets/auto_completion_node_widget.dart';
  6. import 'package:app_flowy/plugins/document/presentation/plugins/openai/widgets/auto_completion_plugins.dart';
  7. import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
  8. import 'package:appflowy_editor/appflowy_editor.dart';
  9. import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
  10. import 'package:flowy_infra_ui/widget/error_page.dart';
  11. import 'package:flutter/material.dart';
  12. import 'package:flutter_bloc/flutter_bloc.dart';
  13. import 'package:intl/intl.dart';
  14. import '../../startup/startup.dart';
  15. import 'application/doc_bloc.dart';
  16. import 'editor_styles.dart';
  17. import 'presentation/banner.dart';
  18. class DocumentPage extends StatefulWidget {
  19. final VoidCallback onDeleted;
  20. final ViewPB view;
  21. DocumentPage({
  22. required this.view,
  23. required this.onDeleted,
  24. Key? key,
  25. }) : super(key: ValueKey(view.id));
  26. @override
  27. State<DocumentPage> createState() => _DocumentPageState();
  28. }
  29. class _DocumentPageState extends State<DocumentPage> {
  30. late DocumentBloc documentBloc;
  31. final FocusNode _focusNode = FocusNode();
  32. @override
  33. void initState() {
  34. // The appflowy editor use Intl as localization, set the default language as fallback.
  35. Intl.defaultLocale = 'en_US';
  36. documentBloc = getIt<DocumentBloc>(param1: super.widget.view)
  37. ..add(const DocumentEvent.initial());
  38. super.initState();
  39. }
  40. @override
  41. Widget build(BuildContext context) {
  42. return MultiBlocProvider(
  43. providers: [
  44. BlocProvider<DocumentBloc>.value(value: documentBloc),
  45. ],
  46. child:
  47. BlocBuilder<DocumentBloc, DocumentState>(builder: (context, state) {
  48. return state.loadingState.map(
  49. loading: (_) => SizedBox.expand(
  50. child: Container(color: Colors.transparent),
  51. ),
  52. finish: (result) => result.successOrFail.fold(
  53. (_) {
  54. if (state.forceClose) {
  55. widget.onDeleted();
  56. return const SizedBox();
  57. } else {
  58. return _renderDocument(context, state);
  59. }
  60. },
  61. (err) => FlowyErrorPage(err.toString()),
  62. ),
  63. );
  64. }),
  65. );
  66. }
  67. @override
  68. Future<void> dispose() async {
  69. documentBloc.close();
  70. _focusNode.dispose();
  71. super.dispose();
  72. }
  73. Widget _renderDocument(BuildContext context, DocumentState state) {
  74. return Column(
  75. children: [
  76. if (state.isDeleted) _renderBanner(context),
  77. // AppFlowy Editor
  78. _renderAppFlowyEditor(
  79. context,
  80. context.read<DocumentBloc>().editorState,
  81. ),
  82. ],
  83. );
  84. }
  85. Widget _renderBanner(BuildContext context) {
  86. return DocumentBanner(
  87. onRestore: () =>
  88. context.read<DocumentBloc>().add(const DocumentEvent.restorePage()),
  89. onDelete: () => context
  90. .read<DocumentBloc>()
  91. .add(const DocumentEvent.deletePermanently()),
  92. );
  93. }
  94. Widget _renderAppFlowyEditor(BuildContext context, EditorState editorState) {
  95. // enable open ai features if needed.
  96. final userProfilePB = context.read<DocumentBloc>().state.userProfilePB;
  97. final openAIKey = userProfilePB?.openaiKey;
  98. final theme = Theme.of(context);
  99. final editor = AppFlowyEditor(
  100. editorState: editorState,
  101. autoFocus: editorState.document.isEmpty,
  102. customBuilders: {
  103. // Divider
  104. kDividerType: DividerWidgetBuilder(),
  105. // Math Equation
  106. kMathEquationType: MathEquationNodeWidgetBuidler(),
  107. // Code Block
  108. kCodeBlockType: CodeBlockNodeWidgetBuilder(),
  109. // Board
  110. kBoardType: BoardNodeWidgetBuilder(),
  111. // Grid
  112. kGridType: GridNodeWidgetBuilder(),
  113. // Card
  114. kCalloutType: CalloutNodeWidgetBuilder(),
  115. // Auto Generator,
  116. kAutoCompletionInputType: AutoCompletionInputBuilder(),
  117. },
  118. shortcutEvents: [
  119. // Divider
  120. insertDividerEvent,
  121. // Code Block
  122. enterInCodeBlock,
  123. ignoreKeysInCodeBlock,
  124. pasteInCodeBlock,
  125. ],
  126. selectionMenuItems: [
  127. // Divider
  128. dividerMenuItem,
  129. // Math Equation
  130. mathEquationMenuItem,
  131. // Code Block
  132. codeBlockMenuItem,
  133. // Emoji
  134. emojiMenuItem,
  135. // Board
  136. boardMenuItem,
  137. // Grid
  138. gridMenuItem,
  139. // Callout
  140. calloutMenuItem,
  141. // AI
  142. if (openAIKey != null && openAIKey.isNotEmpty) ...[
  143. autoGeneratorMenuItem,
  144. ]
  145. ],
  146. themeData: theme.copyWith(extensions: [
  147. ...theme.extensions.values,
  148. customEditorTheme(context),
  149. ...customPluginTheme(context),
  150. ]),
  151. );
  152. return Expanded(
  153. child: Center(
  154. child: Container(
  155. constraints: const BoxConstraints(
  156. maxWidth: double.infinity,
  157. ),
  158. child: editor,
  159. ),
  160. ),
  161. );
  162. }
  163. }