| 
									
										
										
										
											2023-03-20 15:29:17 -10:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/plugins/board/board_view_menu_item.dart'; | 
					
						
							|  |  |  | import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; | 
					
						
							|  |  |  | import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; | 
					
						
							|  |  |  | import 'package:appflowy_editor/appflowy_editor.dart'; | 
					
						
							| 
									
										
										
										
											2023-02-26 16:27:17 +08:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/plugins/board/board_node_widget.dart'; | 
					
						
							| 
									
										
										
										
											2023-03-02 16:34:22 +05:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/plugins/cover/cover_node_widget.dart'; | 
					
						
							| 
									
										
										
										
											2023-02-26 16:27:17 +08:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/plugins/grid/grid_menu_item.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/auto_completion_node_widget.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/auto_completion_plugins.dart'; | 
					
						
							| 
									
										
										
										
											2023-02-28 14:34:13 +08:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/smart_edit_node_widget.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/smart_edit_toolbar_item.dart'; | 
					
						
							| 
									
										
										
										
											2023-03-02 16:34:22 +05:00
										 |  |  | import 'package:dartz/dartz.dart' as dartz; | 
					
						
							| 
									
										
										
										
											2021-10-19 13:56:11 +08:00
										 |  |  | import 'package:flowy_infra_ui/widget/error_page.dart'; | 
					
						
							| 
									
										
										
										
											2021-07-24 18:55:13 +08:00
										 |  |  | import 'package:flutter_bloc/flutter_bloc.dart'; | 
					
						
							| 
									
										
										
										
											2023-03-20 15:29:17 -10:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2022-10-22 21:57:44 +08:00
										 |  |  | import 'package:intl/intl.dart'; | 
					
						
							| 
									
										
										
										
											2022-12-12 15:10:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import '../../startup/startup.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-09 10:35:27 +08:00
										 |  |  | import 'application/doc_bloc.dart'; | 
					
						
							| 
									
										
										
										
											2022-12-12 15:10:38 +08:00
										 |  |  | import 'editor_styles.dart'; | 
					
						
							|  |  |  | import 'presentation/banner.dart'; | 
					
						
							| 
									
										
										
										
											2023-03-21 15:55:20 -10:00
										 |  |  | import 'presentation/plugins/grid/grid_view_menu_item.dart'; | 
					
						
							| 
									
										
										
										
											2023-03-20 15:29:17 -10:00
										 |  |  | import 'presentation/plugins/board/board_menu_item.dart'; | 
					
						
							| 
									
										
										
										
											2021-10-22 23:49:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 22:17:47 +08:00
										 |  |  | class DocumentPage extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2022-09-22 13:08:48 +08:00
										 |  |  |   final VoidCallback onDeleted; | 
					
						
							| 
									
										
										
										
											2022-07-19 14:11:29 +08:00
										 |  |  |   final ViewPB view; | 
					
						
							| 
									
										
										
										
											2021-07-24 14:05:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-22 13:08:48 +08:00
										 |  |  |   DocumentPage({ | 
					
						
							|  |  |  |     required this.view, | 
					
						
							|  |  |  |     required this.onDeleted, | 
					
						
							|  |  |  |     Key? key, | 
					
						
							|  |  |  |   }) : super(key: ValueKey(view.id)); | 
					
						
							| 
									
										
										
										
											2021-07-24 14:05:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-08 16:58:58 +08:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2022-02-23 22:17:47 +08:00
										 |  |  |   State<DocumentPage> createState() => _DocumentPageState(); | 
					
						
							| 
									
										
										
										
											2021-10-08 16:58:58 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 22:17:47 +08:00
										 |  |  | class _DocumentPageState extends State<DocumentPage> { | 
					
						
							|  |  |  |   late DocumentBloc documentBloc; | 
					
						
							| 
									
										
										
										
											2021-10-08 16:58:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-19 13:56:11 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							| 
									
										
										
										
											2022-10-26 10:38:57 +08:00
										 |  |  |     // The appflowy editor use Intl as localization, set the default language as fallback.
 | 
					
						
							| 
									
										
										
										
											2022-10-22 21:57:44 +08:00
										 |  |  |     Intl.defaultLocale = 'en_US'; | 
					
						
							| 
									
										
										
										
											2022-08-09 10:35:27 +08:00
										 |  |  |     documentBloc = getIt<DocumentBloc>(param1: super.widget.view) | 
					
						
							|  |  |  |       ..add(const DocumentEvent.initial()); | 
					
						
							| 
									
										
										
										
											2021-10-19 13:56:11 +08:00
										 |  |  |     super.initState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-21 13:25:46 +08:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2023-02-24 09:16:51 +08:00
										 |  |  |   void dispose() { | 
					
						
							|  |  |  |     documentBloc.close(); | 
					
						
							| 
									
										
										
										
											2023-02-21 13:25:46 +08:00
										 |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 18:55:13 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2021-10-19 13:56:11 +08:00
										 |  |  |     return MultiBlocProvider( | 
					
						
							|  |  |  |       providers: [ | 
					
						
							| 
									
										
										
										
											2022-02-23 22:17:47 +08:00
										 |  |  |         BlocProvider<DocumentBloc>.value(value: documentBloc), | 
					
						
							| 
									
										
										
										
											2021-10-19 13:56:11 +08:00
										 |  |  |       ], | 
					
						
							| 
									
										
										
										
											2022-08-09 10:35:27 +08:00
										 |  |  |       child: | 
					
						
							|  |  |  |           BlocBuilder<DocumentBloc, DocumentState>(builder: (context, state) { | 
					
						
							| 
									
										
										
										
											2022-02-23 22:17:47 +08:00
										 |  |  |         return state.loadingState.map( | 
					
						
							| 
									
										
										
										
											2022-10-22 21:57:44 +08:00
										 |  |  |           loading: (_) => SizedBox.expand( | 
					
						
							|  |  |  |             child: Container(color: Colors.transparent), | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2021-10-19 13:56:11 +08:00
										 |  |  |           finish: (result) => result.successOrFail.fold( | 
					
						
							| 
									
										
										
										
											2021-10-31 20:27:37 +08:00
										 |  |  |             (_) { | 
					
						
							|  |  |  |               if (state.forceClose) { | 
					
						
							| 
									
										
										
										
											2022-09-22 13:08:48 +08:00
										 |  |  |                 widget.onDeleted(); | 
					
						
							|  |  |  |                 return const SizedBox(); | 
					
						
							| 
									
										
										
										
											2023-02-24 09:16:51 +08:00
										 |  |  |               } else if (documentBloc.editorState == null) { | 
					
						
							|  |  |  |                 return const SizedBox(); | 
					
						
							| 
									
										
										
										
											2021-10-31 20:27:37 +08:00
										 |  |  |               } else { | 
					
						
							| 
									
										
										
										
											2022-02-23 22:17:47 +08:00
										 |  |  |                 return _renderDocument(context, state); | 
					
						
							| 
									
										
										
										
											2021-10-31 20:27:37 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |             }, | 
					
						
							| 
									
										
										
										
											2021-10-19 13:56:11 +08:00
										 |  |  |             (err) => FlowyErrorPage(err.toString()), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }), | 
					
						
							| 
									
										
										
										
											2021-07-24 14:05:49 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 22:17:47 +08:00
										 |  |  |   Widget _renderDocument(BuildContext context, DocumentState state) { | 
					
						
							| 
									
										
										
										
											2022-11-14 15:31:39 +08:00
										 |  |  |     return Column( | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         if (state.isDeleted) _renderBanner(context), | 
					
						
							|  |  |  |         // AppFlowy Editor
 | 
					
						
							| 
									
										
										
										
											2023-02-24 09:16:51 +08:00
										 |  |  |         const _AppFlowyEditorPage(), | 
					
						
							| 
									
										
										
										
											2022-11-14 15:31:39 +08:00
										 |  |  |       ], | 
					
						
							| 
									
										
										
										
											2021-10-31 19:48:20 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _renderBanner(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-02-23 22:17:47 +08:00
										 |  |  |     return DocumentBanner( | 
					
						
							| 
									
										
										
										
											2022-08-09 10:35:27 +08:00
										 |  |  |       onRestore: () => | 
					
						
							|  |  |  |           context.read<DocumentBloc>().add(const DocumentEvent.restorePage()), | 
					
						
							|  |  |  |       onDelete: () => context | 
					
						
							|  |  |  |           .read<DocumentBloc>() | 
					
						
							|  |  |  |           .add(const DocumentEvent.deletePermanently()), | 
					
						
							| 
									
										
										
										
											2021-10-31 19:48:20 +08:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-10-08 16:58:58 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-02-24 09:16:51 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-08 16:58:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 09:16:51 +08:00
										 |  |  | class _AppFlowyEditorPage extends StatefulWidget { | 
					
						
							|  |  |  |   const _AppFlowyEditorPage({ | 
					
						
							|  |  |  |     Key? key, | 
					
						
							|  |  |  |   }) : super(key: key); | 
					
						
							| 
									
										
										
										
											2023-02-16 10:17:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 09:16:51 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   State<_AppFlowyEditorPage> createState() => _AppFlowyEditorPageState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _AppFlowyEditorPageState extends State<_AppFlowyEditorPage> { | 
					
						
							|  |  |  |   late DocumentBloc documentBloc; | 
					
						
							|  |  |  |   late EditorState editorState; | 
					
						
							|  |  |  |   String? get openAIKey => documentBloc.state.userProfilePB?.openaiKey; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							|  |  |  |     documentBloc = context.read<DocumentBloc>(); | 
					
						
							|  |  |  |     editorState = documentBloc.editorState ?? EditorState.empty(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-10-25 21:58:44 +08:00
										 |  |  |     final theme = Theme.of(context); | 
					
						
							| 
									
										
										
										
											2023-03-20 13:29:17 +00:00
										 |  |  |     final autoFocusParameters = _autoFocusParameters(); | 
					
						
							| 
									
										
										
										
											2022-10-22 21:57:44 +08:00
										 |  |  |     final editor = AppFlowyEditor( | 
					
						
							|  |  |  |       editorState: editorState, | 
					
						
							| 
									
										
										
										
											2023-03-20 13:29:17 +00:00
										 |  |  |       autoFocus: autoFocusParameters.value1, | 
					
						
							|  |  |  |       focusedSelection: autoFocusParameters.value2, | 
					
						
							| 
									
										
										
										
											2022-10-22 21:57:44 +08:00
										 |  |  |       customBuilders: { | 
					
						
							| 
									
										
										
										
											2022-12-01 19:26:00 +08:00
										 |  |  |         // Divider
 | 
					
						
							|  |  |  |         kDividerType: DividerWidgetBuilder(), | 
					
						
							|  |  |  |         // Math Equation
 | 
					
						
							|  |  |  |         kMathEquationType: MathEquationNodeWidgetBuidler(), | 
					
						
							|  |  |  |         // Code Block
 | 
					
						
							|  |  |  |         kCodeBlockType: CodeBlockNodeWidgetBuilder(), | 
					
						
							| 
									
										
										
										
											2023-01-30 12:22:13 +07:00
										 |  |  |         // Board
 | 
					
						
							|  |  |  |         kBoardType: BoardNodeWidgetBuilder(), | 
					
						
							| 
									
										
										
										
											2023-02-01 14:37:45 +07:00
										 |  |  |         // Grid
 | 
					
						
							|  |  |  |         kGridType: GridNodeWidgetBuilder(), | 
					
						
							| 
									
										
										
										
											2023-01-30 03:56:19 +01:00
										 |  |  |         // Card
 | 
					
						
							|  |  |  |         kCalloutType: CalloutNodeWidgetBuilder(), | 
					
						
							| 
									
										
										
										
											2023-02-16 10:17:08 +08:00
										 |  |  |         // Auto Generator,
 | 
					
						
							|  |  |  |         kAutoCompletionInputType: AutoCompletionInputBuilder(), | 
					
						
							| 
									
										
										
										
											2023-03-02 16:34:22 +05:00
										 |  |  |         // Cover
 | 
					
						
							|  |  |  |         kCoverType: CoverNodeWidgetBuilder(), | 
					
						
							| 
									
										
										
										
											2023-02-28 14:34:13 +08:00
										 |  |  |         // Smart Edit,
 | 
					
						
							|  |  |  |         kSmartEditType: SmartEditInputBuilder(), | 
					
						
							| 
									
										
										
										
											2022-10-22 21:57:44 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       shortcutEvents: [ | 
					
						
							| 
									
										
										
										
											2022-12-01 19:26:00 +08:00
										 |  |  |         // Divider
 | 
					
						
							|  |  |  |         insertDividerEvent, | 
					
						
							|  |  |  |         // Code Block
 | 
					
						
							|  |  |  |         enterInCodeBlock, | 
					
						
							|  |  |  |         ignoreKeysInCodeBlock, | 
					
						
							|  |  |  |         pasteInCodeBlock, | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       selectionMenuItems: [ | 
					
						
							|  |  |  |         // Divider
 | 
					
						
							|  |  |  |         dividerMenuItem, | 
					
						
							|  |  |  |         // Math Equation
 | 
					
						
							|  |  |  |         mathEquationMenuItem, | 
					
						
							|  |  |  |         // Code Block
 | 
					
						
							|  |  |  |         codeBlockMenuItem, | 
					
						
							| 
									
										
										
										
											2022-12-12 15:10:38 +08:00
										 |  |  |         // Emoji
 | 
					
						
							|  |  |  |         emojiMenuItem, | 
					
						
							| 
									
										
										
										
											2023-01-30 12:22:13 +07:00
										 |  |  |         // Board
 | 
					
						
							|  |  |  |         boardMenuItem, | 
					
						
							| 
									
										
										
										
											2023-03-20 15:29:17 -10:00
										 |  |  |         // Create Board
 | 
					
						
							|  |  |  |         boardViewMenuItem(documentBloc), | 
					
						
							| 
									
										
										
										
											2023-02-01 14:37:45 +07:00
										 |  |  |         // Grid
 | 
					
						
							|  |  |  |         gridMenuItem, | 
					
						
							| 
									
										
										
										
											2023-03-21 15:55:20 -10:00
										 |  |  |         // Create Grid
 | 
					
						
							|  |  |  |         gridViewMenuItem(documentBloc), | 
					
						
							| 
									
										
										
										
											2023-02-07 03:03:36 +01:00
										 |  |  |         // Callout
 | 
					
						
							|  |  |  |         calloutMenuItem, | 
					
						
							| 
									
										
										
										
											2023-02-16 10:17:08 +08:00
										 |  |  |         // AI
 | 
					
						
							| 
									
										
										
										
											2023-02-24 09:16:51 +08:00
										 |  |  |         // enable open ai features if needed.
 | 
					
						
							|  |  |  |         if (openAIKey != null && openAIKey!.isNotEmpty) ...[ | 
					
						
							| 
									
										
										
										
											2023-02-16 10:17:08 +08:00
										 |  |  |           autoGeneratorMenuItem, | 
					
						
							| 
									
										
										
										
											2023-03-02 16:34:22 +05:00
										 |  |  |         ], | 
					
						
							| 
									
										
										
										
											2022-10-22 21:57:44 +08:00
										 |  |  |       ], | 
					
						
							| 
									
										
										
										
											2023-02-28 14:34:13 +08:00
										 |  |  |       toolbarItems: [ | 
					
						
							| 
									
										
										
										
											2023-03-14 01:06:08 +08:00
										 |  |  |         smartEditItem, | 
					
						
							| 
									
										
										
										
											2023-02-28 14:34:13 +08:00
										 |  |  |       ], | 
					
						
							| 
									
										
										
										
											2022-10-25 21:58:44 +08:00
										 |  |  |       themeData: theme.copyWith(extensions: [ | 
					
						
							|  |  |  |         ...theme.extensions.values, | 
					
						
							|  |  |  |         customEditorTheme(context), | 
					
						
							|  |  |  |         ...customPluginTheme(context), | 
					
						
							|  |  |  |       ]), | 
					
						
							| 
									
										
										
										
											2021-07-24 14:05:49 +08:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-07-26 16:35:51 +08:00
										 |  |  |     return Expanded( | 
					
						
							| 
									
										
										
										
											2022-11-22 20:06:54 +08:00
										 |  |  |       child: Center( | 
					
						
							|  |  |  |         child: Container( | 
					
						
							| 
									
										
										
										
											2023-01-30 12:22:13 +07:00
										 |  |  |           constraints: const BoxConstraints( | 
					
						
							|  |  |  |             maxWidth: double.infinity, | 
					
						
							| 
									
										
										
										
											2022-11-22 20:06:54 +08:00
										 |  |  |           ), | 
					
						
							|  |  |  |           child: editor, | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2022-02-05 11:15:24 +08:00
										 |  |  |       ), | 
					
						
							| 
									
										
										
										
											2021-07-24 14:05:49 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-02-21 13:25:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 09:16:51 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     _clearTemporaryNodes(); | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-21 13:25:46 +08:00
										 |  |  |   Future<void> _clearTemporaryNodes() async { | 
					
						
							|  |  |  |     final document = editorState.document; | 
					
						
							|  |  |  |     if (document.root.children.isEmpty) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     final temporaryNodeTypes = [ | 
					
						
							|  |  |  |       kAutoCompletionInputType, | 
					
						
							| 
									
										
										
										
											2023-02-28 14:34:13 +08:00
										 |  |  |       kSmartEditType, | 
					
						
							| 
									
										
										
										
											2023-02-21 13:25:46 +08:00
										 |  |  |     ]; | 
					
						
							|  |  |  |     final iterator = NodeIterator( | 
					
						
							|  |  |  |       document: document, | 
					
						
							|  |  |  |       startNode: document.root.children.first, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     final transaction = editorState.transaction; | 
					
						
							|  |  |  |     while (iterator.moveNext()) { | 
					
						
							|  |  |  |       final node = iterator.current; | 
					
						
							|  |  |  |       if (temporaryNodeTypes.contains(node.type)) { | 
					
						
							|  |  |  |         transaction.deleteNode(node); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (transaction.operations.isNotEmpty) { | 
					
						
							|  |  |  |       await editorState.apply(transaction, withUpdateCursor: false); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-03-02 16:34:22 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 13:29:17 +00:00
										 |  |  |   dartz.Tuple2<bool, Selection?> _autoFocusParameters() { | 
					
						
							| 
									
										
										
										
											2023-03-02 16:34:22 +05:00
										 |  |  |     if (editorState.document.isEmpty) { | 
					
						
							|  |  |  |       return dartz.Tuple2(true, Selection.single(path: [0], startOffset: 0)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     final texts = editorState.document.root.children.whereType<TextNode>(); | 
					
						
							|  |  |  |     if (texts.every((element) => element.toPlainText().isEmpty)) { | 
					
						
							|  |  |  |       return dartz.Tuple2( | 
					
						
							|  |  |  |         true, | 
					
						
							|  |  |  |         Selection.single(path: texts.first.path, startOffset: 0), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return const dartz.Tuple2(false, null); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-07-24 14:05:49 +08:00
										 |  |  | } |