From 3f275760664757b67a20e7ff660dc91c259a7a73 Mon Sep 17 00:00:00 2001 From: Haris Arshad Date: Fri, 9 Dec 2022 15:22:43 +0500 Subject: [PATCH] added:emoji added emoji support --- .../appflowy_editor/lib/src/l10n/l10n.dart | 10 + .../src/render/emoji/emoji_select_widget.dart | 186 ++++++++++++++++++ .../selection_menu_service.dart | 9 +- 3 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 frontend/app_flowy/packages/appflowy_editor/lib/src/render/emoji/emoji_select_widget.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart index 0d464022d2..f14d1e67d6 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart @@ -131,6 +131,16 @@ class AppFlowyEditorLocalizations { ); } + /// `Emoji` + String get emoji { + return Intl.message( + 'Emoji', + name: 'emoji', + desc: '', + args: [], + ); + } + /// `Image` String get image { return Intl.message( diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/emoji/emoji_select_widget.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/emoji/emoji_select_widget.dart new file mode 100644 index 0000000000..2ee9398be4 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/emoji/emoji_select_widget.dart @@ -0,0 +1,186 @@ +import 'package:appflowy_editor/src/core/document/node.dart'; +import 'package:appflowy_editor/src/editor_state.dart'; +import 'package:appflowy_editor/src/infra/flowy_svg.dart'; +import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart'; +import 'package:appflowy_editor/src/render/style/editor_style.dart'; +import 'package:flutter/material.dart'; +import 'package:app_flowy/workspace/presentation/widgets/emoji_picker/src/emoji_picker.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; + +import 'package:app_flowy/workspace/presentation/widgets/emoji_picker/src/models/emoji_model.dart'; +import 'package:app_flowy/workspace/presentation/widgets/emoji_picker/src/config.dart'; +import 'package:app_flowy/workspace/presentation/widgets/emoji_picker/src/emoji_button.dart'; +import 'dart:collection'; + +import 'package:appflowy_popover/appflowy_popover.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/foundation.dart' as foundation; + +OverlayEntry? _imojiSelectionMenu; +EditorState? _editorState; +void showEmojiSelectionMenu( + EditorState editorState, + SelectionMenuService menuService, + BuildContext context, +) { + menuService.dismiss(); + + _imojiSelectionMenu?.remove(); + _imojiSelectionMenu = OverlayEntry(builder: (context) { + return Positioned( + top: menuService.topLeft.dy, + left: menuService.topLeft.dx, + child: Material( + child: EmojiSelectionMenu( + editorState: editorState, + onSubmitted: (text) { + // insert emoji + editorState.insertEmojiNode(text); + }, + onExit: () { + _dismissEmojiSelectionMenu(); + //close emoji panel + }, + ), + ), + ); + }); + + Overlay.of(context)?.insert(_imojiSelectionMenu!); + + editorState.service.selectionService.currentSelection + .addListener(_dismissEmojiSelectionMenu); +} + +void _dismissEmojiSelectionMenu() { + _imojiSelectionMenu?.remove(); + _imojiSelectionMenu = null; + + _editorState?.service.selectionService.currentSelection + .removeListener(_dismissEmojiSelectionMenu); + _editorState = null; +} + +class EmojiSelectionMenu extends StatefulWidget { + const EmojiSelectionMenu({ + Key? key, + required this.onSubmitted, + required this.onExit, + this.editorState, + }) : super(key: key); + + final void Function(Emoji Emoji) onSubmitted; + final void Function() onExit; + final EditorState? editorState; + + @override + State createState() => _EmojiSelectionMenuState(); +} + +class _EmojiSelectionMenuState extends State { + EditorStyle? get style => widget.editorState?.editorStyle; + + @override + void initState() { + HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent); + super.initState(); + } + + bool _handleGlobalKeyEvent(KeyEvent event) { + if (event.logicalKey == LogicalKeyboardKey.escape && + event is KeyDownEvent) { + widget.onExit(); + return true; + } else { + return false; + } + } + + @override + void deactivate() { + HardwareKeyboard.instance.removeHandler(_handleGlobalKeyEvent); + super.deactivate(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + width: 300, + padding: const EdgeInsets.all(24.0), + decoration: BoxDecoration( + color: style?.selectionMenuBackgroundColor ?? Colors.white, + boxShadow: [ + BoxShadow( + blurRadius: 5, + spreadRadius: 1, + color: Colors.black.withOpacity(0.1), + ), + ], + // borderRadius: BorderRadius.circular(6.0), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeader(context), + FlowyEmojiStyleButton(normalIcon: '', tooltipText: ''), + const SizedBox(height: 10.0), + _buildEmojiBox(context), + ], + ), + ); + } + + Widget _buildHeader(BuildContext context) { + return Text( + 'Pick Emoji', + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 14.0, + color: style?.selectionMenuItemTextColor ?? Colors.black, + fontWeight: FontWeight.w500, + ), + ); + } + + Widget _buildEmojiBox(BuildContext context) { + return SizedBox( + height: 300, + child: EmojiPicker( + onEmojiSelected: (category, emoji) => widget.onSubmitted(emoji), + config: const Config( + columns: 8, + emojiSizeMax: 28, + bgColor: Color(0xffF2F2F2), + iconColor: Colors.grey, + iconColorSelected: Color(0xff333333), + indicatorColor: Color(0xff333333), + progressIndicatorColor: Color(0xff333333), + buttonMode: ButtonMode.CUPERTINO, + initCategory: Category.RECENT, + ), + ), + ); + } +} + +extension on EditorState { + void insertEmojiNode(Emoji emoji) { + final selectionService = service.selectionService; + final currentSelection = selectionService.currentSelection.value; + + if (currentSelection == null) { + return; + } + + final textNode = selectionService.currentSelectedNodes.first as TextNode; + final transaction = this.transaction; + transaction.insertText(textNode, currentSelection.endIndex, emoji.emoji); + apply(transaction); + } +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart index 402e7cee84..e448b1f9cc 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart @@ -4,7 +4,7 @@ import 'package:appflowy_editor/src/l10n/l10n.dart'; import 'package:appflowy_editor/src/render/image/image_upload_widget.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart'; import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; - +import 'package:appflowy_editor/src/render/emoji/emoji_select_widget.dart'; import 'package:flutter/material.dart'; import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; @@ -181,6 +181,13 @@ final List _defaultSelectionMenuItems = [ keywords: ['image'], handler: showImageUploadMenu, ), + SelectionMenuItem( + name: () => AppFlowyEditorLocalizations.current.emoji, + icon: (editorState, onSelected) => + _selectionMenuIcon('image', editorState, onSelected), + keywords: ['emoji'], + handler: showEmojiSelectionMenu, + ), SelectionMenuItem( name: () => AppFlowyEditorLocalizations.current.bulletedList, icon: (editorState, onSelected) =>