From 4f6a92e68488ce0e850fc874af6521fdf9cd7bc1 Mon Sep 17 00:00:00 2001 From: Sanjay-Sharma Date: Tue, 13 Jun 2023 09:08:40 +0000 Subject: [PATCH] [IMP] web_editor: add emoji picker in web editor Purpose: This commit adds Emoji_picker functionality to the web_editor, allowing users to conveniently add emojis to their text and improving the overall user experience. Specifications: Now we can insert emoji in web_editor using `/emoji`. Task id: 3253958 closes odoo/odoo#125137 Related: odoo/enterprise#42772 Signed-off-by: Simon Genin (ges@odoo) --- addons/im_livechat/__manifest__.py | 2 +- .../src/embed/core_ui/emoji_picker.scss | 2 +- .../static/tests/composer_patch_tests.js | 2 +- addons/mail/__manifest__.py | 7 +-- .../static/src/core/common/chat_window.js | 2 +- .../mail/static/src/core/common/composer.js | 7 +-- addons/mail/static/src/core/common/message.js | 2 +- .../static/src/core/common/navigable_list.js | 2 +- .../static/src/core/common/thread_service.js | 4 +- .../mail/static/src/core/web/message_patch.js | 2 +- addons/mail/static/src/core/web/sidebar.js | 2 +- .../static/src/discuss/call/common/call.js | 2 +- .../call/common/call_participant_card.js | 2 +- .../src/discuss/core/web/channel_selector.js | 3 +- .../src/discuss/core/web/message_patch.js | 2 +- .../gif_picker/common/composer_patch.js | 2 +- .../discuss/gif_picker/common/gif_picker.js | 2 +- addons/mail/static/src/utils/common/misc.js | 30 ----------- .../tests/chat_window/chat_window_tests.js | 2 +- .../static/tests/composer/composer_tests.js | 36 ++++++------- .../tests/discuss/core/composer_tests.js | 2 +- .../static/tests/discuss_app/inbox_tests.js | 4 +- addons/mail/static/tests/emoji/emoji_tests.js | 50 +++++++++---------- .../static/tests/message/message_tests.js | 14 +++--- .../tests/web/fields/emojis_fields_tests.js | 6 +-- addons/point_of_sale/__manifest__.py | 1 + addons/pos_self_order/__manifest__.py | 1 + addons/project/__manifest__.py | 1 + addons/web/__manifest__.py | 5 ++ .../src/core/emoji_picker/emoji_picker.js | 15 +++--- .../src/core/emoji_picker/emoji_picker.scss | 22 ++++---- .../src/core/emoji_picker/emoji_picker.xml | 40 +++++++-------- addons/web/static/src/core/utils/misc.js | 29 +++++++++++ .../components/emoji_popover/emoji_popover.js | 19 +++++++ .../static/src/js/wysiwyg/wysiwyg.js | 39 +++++++++++++++ addons/website_slides/__manifest__.py | 1 + 36 files changed, 213 insertions(+), 151 deletions(-) create mode 100644 addons/web/static/src/core/utils/misc.js create mode 100644 addons/web_editor/static/src/components/emoji_popover/emoji_popover.js diff --git a/addons/im_livechat/__manifest__.py b/addons/im_livechat/__manifest__.py index 431376faa7e..0f6fddc4d28 100644 --- a/addons/im_livechat/__manifest__.py +++ b/addons/im_livechat/__manifest__.py @@ -98,7 +98,6 @@ Help your customers with this chat, and analyse their feedback. 'mail/static/src/core/common/**/*', 'mail/static/src/utils/common/**/*', ('remove', 'mail/static/src/**/*.dark.scss'), - ('remove', 'mail/static/src/core/common/emoji_data.js'), 'im_livechat/static/src/embed/**/*', 'im_livechat/static/src/embed/livechat_data.js', @@ -118,6 +117,7 @@ Help your customers with this chat, and analyse their feedback. 'web/static/src/env.js', ('remove', 'web/static/src/legacy/js/services/ajax_service.js'), ('remove', 'web/static/src/legacy/js/common_env.js'), + ('remove', 'web/static/src/core/emoji_picker/emoji_data.js'), 'web/static/src/views/fields/formatters.js', 'web/static/src/views/fields/file_handler.*', 'bus/static/src/bus_parameters_service.js', diff --git a/addons/im_livechat/static/src/embed/core_ui/emoji_picker.scss b/addons/im_livechat/static/src/embed/core_ui/emoji_picker.scss index 85315029fcf..70697dbd4b4 100644 --- a/addons/im_livechat/static/src/embed/core_ui/emoji_picker.scss +++ b/addons/im_livechat/static/src/embed/core_ui/emoji_picker.scss @@ -1,3 +1,3 @@ -.o-mail-EmojiPicker { +.o-EmojiPicker { font-size: 1.2em; } diff --git a/addons/im_livechat/static/tests/composer_patch_tests.js b/addons/im_livechat/static/tests/composer_patch_tests.js index a5966b911ec..12508ec2437 100644 --- a/addons/im_livechat/static/tests/composer_patch_tests.js +++ b/addons/im_livechat/static/tests/composer_patch_tests.js @@ -219,7 +219,7 @@ QUnit.test("add an emoji after a canned response", async (assert) => { "previous content + canned response substitution + additional whitespace afterwards" ); await click("button[aria-label='Emojis']"); - await click(".o-mail-Emoji:contains(😊)"); + await click(".o-Emoji:contains(😊)"); assert.strictEqual( $(".o-mail-Composer-input").val().replace(/\s/, " "), "Hello! How are you? 😊" diff --git a/addons/mail/__manifest__.py b/addons/mail/__manifest__.py index 4e35b0eb595..e676fb37c32 100644 --- a/addons/mail/__manifest__.py +++ b/addons/mail/__manifest__.py @@ -132,7 +132,6 @@ For more specific needs, you may also assign custom-defined actions 'mail/static/src/**/common/**/*', 'mail/static/src/**/web/**/*', ('remove', 'mail/static/src/**/*.dark.scss'), - ('remove', 'mail/static/src/core/common/emoji_data.js'), # discuss (loaded last to fix dependencies) ('remove', 'mail/static/src/discuss/**/*'), 'mail/static/src/discuss/core/common/**/*', @@ -160,9 +159,7 @@ For more specific needs, you may also assign custom-defined actions ('remove', 'mail/static/tests/tours/**/*'), ('remove', 'mail/static/tests/helpers/**/*'), ], - 'mail.assets_emoji': [ - 'mail/static/src/core/common/emoji_data.js', - ], + 'mail.assets_public': [ ('include', 'web._assets_helpers'), ('include', 'web._assets_backend_helpers'), @@ -174,6 +171,7 @@ For more specific needs, you may also assign custom-defined actions 'web/static/src/core/**/*', 'web/static/src/env.js', ('remove', 'web/static/src/legacy/js/services/ajax_service.js'), + ('remove', 'web/static/src/core/emoji_picker/emoji_data.js'), 'web/static/src/views/fields/formatters.js', 'web/static/src/views/fields/file_handler.*', @@ -189,7 +187,6 @@ For more specific needs, you may also assign custom-defined actions # 'mail/static/src/core/public/**/*', 'mail/static/src/**/common/**/*', 'mail/static/src/**/public/**/*', - ('remove', 'mail/static/src/core/common/emoji_data.js'), ('remove', 'mail/static/src/**/*.dark.scss'), # discuss (loaded last to fix dependencies) ('remove', 'mail/static/src/discuss/**/*'), diff --git a/addons/mail/static/src/core/common/chat_window.js b/addons/mail/static/src/core/common/chat_window.js index 6d7c2d4bd13..da19af6b21c 100644 --- a/addons/mail/static/src/core/common/chat_window.js +++ b/addons/mail/static/src/core/common/chat_window.js @@ -12,7 +12,7 @@ import { useMessageHighlight, useMessageToReplyTo, } from "@mail/utils/common/hooks"; -import { isEventHandled } from "@mail/utils/common/misc"; +import { isEventHandled } from "@web/core/utils/misc"; import { Component, useChildSubEnv, useRef, useState } from "@odoo/owl"; diff --git a/addons/mail/static/src/core/common/composer.js b/addons/mail/static/src/core/common/composer.js index b52a718ca7e..01eca912afb 100644 --- a/addons/mail/static/src/core/common/composer.js +++ b/addons/mail/static/src/core/common/composer.js @@ -10,11 +10,8 @@ import { NavigableList } from "@mail/core/common/navigable_list"; import { useSuggestion } from "@mail/core/common/suggestion_hook"; import { escapeAndCompactTextContent } from "@mail/utils/common/format"; import { useSelection } from "@mail/utils/common/hooks"; -import { - isDragSourceExternalFile, - isEventHandled, - markEventHandled, -} from "@mail/utils/common/misc"; +import { isDragSourceExternalFile } from "@mail/utils/common/misc"; +import { isEventHandled, markEventHandled } from "@web/core/utils/misc"; import { Component, diff --git a/addons/mail/static/src/core/common/message.js b/addons/mail/static/src/core/common/message.js index bbd798f163c..4624b6d8db5 100644 --- a/addons/mail/static/src/core/common/message.js +++ b/addons/mail/static/src/core/common/message.js @@ -14,7 +14,7 @@ import { MessageSeenIndicator } from "@mail/core/common/message_seen_indicator"; import { useMessaging, useStore } from "@mail/core/common/messaging_hook"; import { RelativeTime } from "@mail/core/common/relative_time"; import { convertBrToLineBreak, htmlToTextContentInline } from "@mail/utils/common/format"; -import { isEventHandled, markEventHandled } from "@mail/utils/common/misc"; +import { isEventHandled, markEventHandled } from "@web/core/utils/misc"; import { Component, diff --git a/addons/mail/static/src/core/common/navigable_list.js b/addons/mail/static/src/core/common/navigable_list.js index 87f90f78f1e..97a904bcadf 100644 --- a/addons/mail/static/src/core/common/navigable_list.js +++ b/addons/mail/static/src/core/common/navigable_list.js @@ -2,7 +2,7 @@ import { ImStatus } from "@mail/core/common/im_status"; import { onExternalClick } from "@mail/utils/common/hooks"; -import { markEventHandled, isEventHandled } from "@mail/utils/common/misc"; +import { markEventHandled, isEventHandled } from "@web/core/utils/misc"; import { Component, useEffect, useExternalListener, useRef, useState } from "@odoo/owl"; diff --git a/addons/mail/static/src/core/common/thread_service.js b/addons/mail/static/src/core/common/thread_service.js index add96a3b7d9..c0a1a22b426 100644 --- a/addons/mail/static/src/core/common/thread_service.js +++ b/addons/mail/static/src/core/common/thread_service.js @@ -928,7 +928,7 @@ export class ThreadService { const prettyContent = await prettifyMessageContent(body, params.validMentions); const { emojis } = await loadEmoji(); const recentEmojis = JSON.parse( - browser.localStorage.getItem("mail.emoji.frequent") || "{}" + browser.localStorage.getItem("web.emoji.frequent") || "{}" ); const emojisInContent = prettyContent.match(/\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu) ?? []; @@ -938,7 +938,7 @@ export class ThreadService { recentEmojis[codepoints]++; } } - browser.localStorage.setItem("mail.emoji.frequent", JSON.stringify(recentEmojis)); + browser.localStorage.setItem("web.emoji.frequent", JSON.stringify(recentEmojis)); tmpMsg = this.messageService.insert({ ...tmpData, body: markup(prettyContent), diff --git a/addons/mail/static/src/core/web/message_patch.js b/addons/mail/static/src/core/web/message_patch.js index 62b9074d11b..39f9232eadf 100644 --- a/addons/mail/static/src/core/web/message_patch.js +++ b/addons/mail/static/src/core/web/message_patch.js @@ -1,7 +1,7 @@ /* @odoo-module */ import { Message } from "@mail/core/common/message"; -import { markEventHandled } from "@mail/utils/common/misc"; +import { markEventHandled } from "@web/core/utils/misc"; import { getCurrency } from "@web/core/currency"; import { deserializeDateTime } from "@web/core/l10n/dates"; diff --git a/addons/mail/static/src/core/web/sidebar.js b/addons/mail/static/src/core/web/sidebar.js index a8a4da37ff3..df10e8e1f5d 100644 --- a/addons/mail/static/src/core/web/sidebar.js +++ b/addons/mail/static/src/core/web/sidebar.js @@ -4,7 +4,7 @@ import { ImStatus } from "@mail/core/common/im_status"; import { useMessaging, useStore } from "@mail/core/common/messaging_hook"; import { ThreadIcon } from "@mail/core/common/thread_icon"; import { onExternalClick } from "@mail/utils/common/hooks"; -import { markEventHandled } from "@mail/utils/common/misc"; +import { markEventHandled } from "@web/core/utils/misc"; import { Component, useState } from "@odoo/owl"; diff --git a/addons/mail/static/src/discuss/call/common/call.js b/addons/mail/static/src/discuss/call/common/call.js index d869440a8f7..f4557123818 100644 --- a/addons/mail/static/src/discuss/call/common/call.js +++ b/addons/mail/static/src/discuss/call/common/call.js @@ -4,7 +4,7 @@ import { useMessaging, useStore } from "@mail/core/common/messaging_hook"; import { CallActionList } from "@mail/discuss/call/common/call_action_list"; import { CallParticipantCard } from "@mail/discuss/call/common/call_participant_card"; import { useRtc } from "@mail/discuss/call/common/rtc_hook"; -import { isEventHandled, markEventHandled } from "@mail/utils/common/misc"; +import { isEventHandled, markEventHandled } from "@web/core/utils/misc"; import { Component, diff --git a/addons/mail/static/src/discuss/call/common/call_participant_card.js b/addons/mail/static/src/discuss/call/common/call_participant_card.js index 28540376abc..598043f136c 100644 --- a/addons/mail/static/src/discuss/call/common/call_participant_card.js +++ b/addons/mail/static/src/discuss/call/common/call_participant_card.js @@ -5,7 +5,7 @@ import { CallContextMenu } from "@mail/discuss/call/common/call_context_menu"; import { CallParticipantVideo } from "@mail/discuss/call/common/call_participant_video"; import { useRtc } from "@mail/discuss/call/common/rtc_hook"; import { useHover } from "@mail/utils/common/hooks"; -import { isEventHandled, markEventHandled } from "@mail/utils/common/misc"; +import { isEventHandled, markEventHandled } from "@web/core/utils/misc"; import { Component, onMounted, onWillUnmount, useRef } from "@odoo/owl"; diff --git a/addons/mail/static/src/discuss/core/web/channel_selector.js b/addons/mail/static/src/discuss/core/web/channel_selector.js index a93946c3c20..1ece8cb8f3e 100644 --- a/addons/mail/static/src/discuss/core/web/channel_selector.js +++ b/addons/mail/static/src/discuss/core/web/channel_selector.js @@ -4,7 +4,8 @@ import { useStore } from "@mail/core/common/messaging_hook"; import { NavigableList } from "@mail/core/common/navigable_list"; import { DEBOUNCE_FETCH_SUGGESTION_TIME } from "@mail/core/common/suggestion_service"; import { cleanTerm } from "@mail/utils/common/format"; -import { createLocalId, isEventHandled, markEventHandled } from "@mail/utils/common/misc"; +import { createLocalId } from "@mail/utils/common/misc"; +import { isEventHandled, markEventHandled } from "@web/core/utils/misc"; import { Component, onMounted, useRef, useState } from "@odoo/owl"; diff --git a/addons/mail/static/src/discuss/core/web/message_patch.js b/addons/mail/static/src/discuss/core/web/message_patch.js index 20169268bbf..6d8f49c5325 100644 --- a/addons/mail/static/src/discuss/core/web/message_patch.js +++ b/addons/mail/static/src/discuss/core/web/message_patch.js @@ -2,7 +2,7 @@ import { Message } from "@mail/core/common/message"; import "@mail/core/web/message_patch"; // dependency ordering -import { markEventHandled } from "@mail/utils/common/misc"; +import { markEventHandled } from "@web/core/utils/misc"; import { _t } from "@web/core/l10n/translation"; import { patch } from "@web/core/utils/patch"; diff --git a/addons/mail/static/src/discuss/gif_picker/common/composer_patch.js b/addons/mail/static/src/discuss/gif_picker/common/composer_patch.js index 25b711e6662..2aef757ff7b 100644 --- a/addons/mail/static/src/discuss/gif_picker/common/composer_patch.js +++ b/addons/mail/static/src/discuss/gif_picker/common/composer_patch.js @@ -3,7 +3,7 @@ import { GifPicker, useGifPicker } from "@mail/discuss/gif_picker/common/gif_picker"; import { Composer } from "@mail/core/common/composer"; import { onExternalClick } from "@mail/utils/common/hooks"; -import { isEventHandled, markEventHandled } from "@mail/utils/common/misc"; +import { isEventHandled, markEventHandled } from "@web/core/utils/misc"; import { useState } from "@odoo/owl"; diff --git a/addons/mail/static/src/discuss/gif_picker/common/gif_picker.js b/addons/mail/static/src/discuss/gif_picker/common/gif_picker.js index 9618c59d68b..cb2f363066c 100644 --- a/addons/mail/static/src/discuss/gif_picker/common/gif_picker.js +++ b/addons/mail/static/src/discuss/gif_picker/common/gif_picker.js @@ -3,7 +3,7 @@ import { useStore } from "@mail/core/common/messaging_hook"; import { removeFromArrayWithPredicate } from "@mail/utils/common/arrays"; import { useOnBottomScrolled } from "@mail/utils/common/hooks"; -import { markEventHandled } from "@mail/utils/common/misc"; +import { markEventHandled } from "@web/core/utils/misc";; import { Component, onWillStart, useRef, useState } from "@odoo/owl"; diff --git a/addons/mail/static/src/utils/common/misc.js b/addons/mail/static/src/utils/common/misc.js index 2f43051b7bb..b8813c48d8f 100644 --- a/addons/mail/static/src/utils/common/misc.js +++ b/addons/mail/static/src/utils/common/misc.js @@ -39,36 +39,6 @@ export function isDragSourceExternalFile(dataTransfer) { return false; } -const eventHandledWeakMap = new WeakMap(); - -/** - * Returns whether the given event has been handled with the given markName. - * - * @param {Event} ev - * @param {string} markName - * @returns {boolean} - */ -export function isEventHandled(ev, markName) { - if (!eventHandledWeakMap.get(ev)) { - return false; - } - return eventHandledWeakMap.get(ev).includes(markName); -} - -/** - * Marks the given event as handled by the given markName. Useful to allow - * handlers in the propagation chain to make a decision based on what has - * already been done. - * - * @param {Event} ev - * @param {string} markName - */ -export function markEventHandled(ev, markName) { - if (!eventHandledWeakMap.get(ev)) { - eventHandledWeakMap.set(ev, []); - } - eventHandledWeakMap.get(ev).push(markName); -} /** * @param {Object} target diff --git a/addons/mail/static/tests/chat_window/chat_window_tests.js b/addons/mail/static/tests/chat_window/chat_window_tests.js index a84b803f61d..75e85c41fd8 100644 --- a/addons/mail/static/tests/chat_window/chat_window_tests.js +++ b/addons/mail/static/tests/chat_window/chat_window_tests.js @@ -279,7 +279,7 @@ QUnit.test( await start(); await click("button[aria-label='Emojis']"); await afterNextRender(() => triggerHotkey("Escape")); - assert.containsNone($, ".o-mail-EmojiPicker"); + assert.containsNone($, ".o-EmojiPicker"); assert.containsOnce($, ".o-mail-ChatWindow"); } ); diff --git a/addons/mail/static/tests/composer/composer_tests.js b/addons/mail/static/tests/composer/composer_tests.js index e240e8e670a..73b72ab23db 100644 --- a/addons/mail/static/tests/composer/composer_tests.js +++ b/addons/mail/static/tests/composer/composer_tests.js @@ -97,7 +97,7 @@ QUnit.test("add an emoji", async (assert) => { const { openDiscuss } = await start(); await openDiscuss(channelId); await click("button[aria-label='Emojis']"); - await click(".o-mail-Emoji:contains(😤)"); + await click(".o-Emoji:contains(😤)"); assert.strictEqual($(".o-mail-Composer-input").val(), "😤"); }); @@ -123,7 +123,7 @@ QUnit.test("add an emoji after some text", async (assert) => { assert.strictEqual($(".o-mail-Composer-input").val(), "Blabla"); await click("button[aria-label='Emojis']"); - await click(".o-mail-Emoji:contains(🤑)"); + await click(".o-Emoji:contains(🤑)"); assert.strictEqual($(".o-mail-Composer-input").val(), "Blabla🤑"); }); @@ -139,7 +139,7 @@ QUnit.test("add emoji replaces (keyboard) text selection", async (assert) => { // simulate selection of all the content by keyboard textarea.setSelectionRange(0, textarea.value.length); await click("button[aria-label='Emojis']"); - await click(".o-mail-Emoji:contains(🤠)"); + await click(".o-Emoji:contains(🤠)"); assert.strictEqual($(".o-mail-Composer-input").val(), "🤠"); }); @@ -152,7 +152,7 @@ QUnit.test("Cursor is positioned after emoji after adding it", async (assert) => await insertText(".o-mail-Composer-input", "Blabla"); textarea.setSelectionRange(2, 2); await click("button[aria-label='Emojis']"); - await click(".o-mail-Emoji:contains(🤠)"); + await click(".o-Emoji:contains(🤠)"); const expectedPos = 2 + "🤠".length; assert.strictEqual(textarea.selectionStart, expectedPos); assert.strictEqual(textarea.selectionEnd, expectedPos); @@ -172,7 +172,7 @@ QUnit.test("selected text is not replaced after cancelling the selection", async $(".o-mail-Discuss-content")[0].click(); await nextTick(); await click("button[aria-label='Emojis']"); - await click(".o-mail-Emoji:contains(🤠)"); + await click(".o-Emoji:contains(🤠)"); assert.strictEqual($(".o-mail-Composer-input").val(), "Blabla🤠"); }); @@ -205,9 +205,9 @@ QUnit.test( const { openDiscuss } = await start(); await openDiscuss(channelId); await click("button[aria-label='Emojis']"); - await click(".o-mail-Emoji:contains(👺)"); + await click(".o-Emoji:contains(👺)"); await click("button[aria-label='Emojis']"); - assert.containsOnce($, ".o-mail-EmojiPicker"); + assert.containsOnce($, ".o-EmojiPicker"); } ); @@ -217,10 +217,10 @@ QUnit.test("keep emoji picker scroll value when re-opening it", async (assert) = const { openDiscuss } = await start(); await openDiscuss(channelId); await click("button[aria-label='Emojis']"); - $(".o-mail-EmojiPicker-content")[0].scrollTop = 150; + $(".o-EmojiPicker-content")[0].scrollTop = 150; await click("button[aria-label='Emojis']"); await click("button[aria-label='Emojis']"); - assert.strictEqual($(".o-mail-EmojiPicker-content")[0].scrollTop, 150); + assert.strictEqual($(".o-EmojiPicker-content")[0].scrollTop, 150); }); QUnit.test("reset emoji picker scroll value after an emoji is picked", async (assert) => { @@ -229,10 +229,10 @@ QUnit.test("reset emoji picker scroll value after an emoji is picked", async (as const { openDiscuss } = await start(); await openDiscuss(channelId); await click("button[aria-label='Emojis']"); - $(".o-mail-EmojiPicker-content")[0].scrollTop = 150; - await click(".o-mail-Emoji:contains(😎)"); + $(".o-EmojiPicker-content")[0].scrollTop = 150; + await click(".o-Emoji:contains(😎)"); await click("button[aria-label='Emojis']"); - assert.strictEqual($(".o-mail-EmojiPicker-content")[0].scrollTop, 0); + assert.strictEqual($(".o-EmojiPicker-content")[0].scrollTop, 0); }); QUnit.test( @@ -253,19 +253,19 @@ QUnit.test( await triggerEvent(getFixture(), null, "mousedown"); await click("button[aria-label='Emojis']"); - $(".o-mail-EmojiPicker-content")[0].scrollTop = 150; + $(".o-EmojiPicker-content")[0].scrollTop = 150; await triggerEvent(getFixture(), null, "mousedown"); await click("[title='Add a Reaction']"); - $(".o-mail-EmojiPicker-content")[0].scrollTop = 200; + $(".o-EmojiPicker-content")[0].scrollTop = 200; await triggerEvent(getFixture(), null, "mousedown"); await click("button[aria-label='Emojis']"); - assert.strictEqual($(".o-mail-EmojiPicker-content")[0].scrollTop, 150); + assert.strictEqual($(".o-EmojiPicker-content")[0].scrollTop, 150); await triggerEvent(getFixture(), null, "mousedown"); await click("[title='Add a Reaction']"); - assert.strictEqual($(".o-mail-EmojiPicker-content")[0].scrollTop, 200); + assert.strictEqual($(".o-EmojiPicker-content")[0].scrollTop, 200); } ); @@ -374,7 +374,7 @@ QUnit.test("add an emoji after a partner mention", async (assert) => { assert.strictEqual($(".o-mail-Composer-input").val().replace(/\s/, " "), "@TestPartner "); await click("button[aria-label='Emojis']"); - await click(".o-mail-Emoji:contains(😊)"); + await click(".o-Emoji:contains(😊)"); assert.strictEqual($(".o-mail-Composer-input").val().replace(/\s/, " "), "@TestPartner 😊"); }); @@ -431,7 +431,7 @@ QUnit.test("add an emoji after a channel mention", async (assert) => { // select emoji await click("button[aria-label='Emojis']"); - await click(".o-mail-Emoji:contains(😊)"); + await click(".o-Emoji:contains(😊)"); assert.strictEqual($(".o-mail-Composer-input").val().replace(/\s/, " "), "#General 😊"); }); diff --git a/addons/mail/static/tests/discuss/core/composer_tests.js b/addons/mail/static/tests/discuss/core/composer_tests.js index 08b2ec8553c..51f7343bb64 100644 --- a/addons/mail/static/tests/discuss/core/composer_tests.js +++ b/addons/mail/static/tests/discuss/core/composer_tests.js @@ -70,6 +70,6 @@ QUnit.test("add an emoji after a command", async (assert) => { ); await click("button[aria-label='Emojis']"); - await click(".o-mail-Emoji:contains(😊)"); + await click(".o-Emoji:contains(😊)"); assert.strictEqual($(".o-mail-Composer-input").val().replace(/\s/, " "), "/who 😊"); }); diff --git a/addons/mail/static/tests/discuss_app/inbox_tests.js b/addons/mail/static/tests/discuss_app/inbox_tests.js index 051ec80581d..c56b3c6331e 100644 --- a/addons/mail/static/tests/discuss_app/inbox_tests.js +++ b/addons/mail/static/tests/discuss_app/inbox_tests.js @@ -72,9 +72,9 @@ QUnit.test("reply: discard on pressing escape", async (assert) => { // Escape on emoji picker does not stop replying await click(".o-mail-Composer button[aria-label='Emojis']"); - assert.containsOnce($, ".o-mail-EmojiPicker"); + assert.containsOnce($, ".o-EmojiPicker"); await afterNextRender(() => triggerHotkey("Escape")); - assert.containsNone($, ".o-mail-EmojiPicker"); + assert.containsNone($, ".o-EmojiPicker"); assert.containsOnce($, ".o-mail-Composer"); // Escape on suggestion prompt does not stop replying diff --git a/addons/mail/static/tests/emoji/emoji_tests.js b/addons/mail/static/tests/emoji/emoji_tests.js index 9fe144b257c..b78a80caa59 100644 --- a/addons/mail/static/tests/emoji/emoji_tests.js +++ b/addons/mail/static/tests/emoji/emoji_tests.js @@ -20,7 +20,7 @@ QUnit.test("search emoji from keywords", async (assert) => { await openDiscuss(channelId); await click("button[aria-label='Emojis']"); await insertText("input[placeholder='Search for an emoji']", "mexican"); - assert.containsOnce($, ".o-mail-Emoji:contains(🌮)"); + assert.containsOnce($, ".o-Emoji:contains(🌮)"); }); QUnit.test("search emoji from keywords should be case insensitive", async (assert) => { @@ -30,7 +30,7 @@ QUnit.test("search emoji from keywords should be case insensitive", async (asser await openDiscuss(channelId); await click("button[aria-label='Emojis']"); await insertText("input[placeholder='Search for an emoji']", "ok"); - assert.containsOnce($, ".o-mail-Emoji:contains(🆗)"); // all search terms are uppercase OK + assert.containsOnce($, ".o-Emoji:contains(🆗)"); // all search terms are uppercase OK }); QUnit.test("search emoji from keywords with special regex character", async (assert) => { @@ -40,7 +40,7 @@ QUnit.test("search emoji from keywords with special regex character", async (ass await openDiscuss(channelId); await click("button[aria-label='Emojis']"); await insertText("input[placeholder='Search for an emoji']", "(blood"); - assert.containsOnce($, ".o-mail-Emoji:contains(🆎)"); + assert.containsOnce($, ".o-Emoji:contains(🆎)"); }); QUnit.test("Press Escape in emoji picker closes the emoji picker", async (assert) => { @@ -50,7 +50,7 @@ QUnit.test("Press Escape in emoji picker closes the emoji picker", async (assert await openDiscuss(channelId); await click("button[aria-label='Emojis']"); await afterNextRender(() => triggerHotkey("Escape")); - assert.containsNone($, ".o-mail-EmojiPicker"); + assert.containsNone($, ".o-EmojiPicker"); }); QUnit.test("Basic keyboard navigation", async (assert) => { @@ -59,22 +59,22 @@ QUnit.test("Basic keyboard navigation", async (assert) => { const { openDiscuss } = await start(); await openDiscuss(channelId); await click("button[aria-label='Emojis']"); - assert.containsOnce($, ".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=0].bg-200"); // bg-200 means active + assert.containsOnce($, ".o-EmojiPicker-content .o-Emoji[data-index=0].bg-200"); // bg-200 means active await afterNextRender(() => triggerHotkey("ArrowRight")); - assert.containsOnce($, ".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=1].bg-200"); + assert.containsOnce($, ".o-EmojiPicker-content .o-Emoji[data-index=1].bg-200"); await afterNextRender(() => triggerHotkey("ArrowDown")); assert.containsOnce( $, - `.o-mail-EmojiPicker-content .o-mail-Emoji[data-index=${EMOJI_PER_ROW + 1}].bg-200` + `.o-EmojiPicker-content .o-Emoji[data-index=${EMOJI_PER_ROW + 1}].bg-200` ); await afterNextRender(() => triggerHotkey("ArrowLeft")); assert.containsOnce( $, - `.o-mail-EmojiPicker-content .o-mail-Emoji[data-index=${EMOJI_PER_ROW}].bg-200` + `.o-EmojiPicker-content .o-Emoji[data-index=${EMOJI_PER_ROW}].bg-200` ); await afterNextRender(() => triggerHotkey("ArrowUp")); - assert.containsOnce($, ".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=0].bg-200"); - const codepoints = $(".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=0].bg-200").data( + assert.containsOnce($, ".o-EmojiPicker-content .o-Emoji[data-index=0].bg-200"); + const codepoints = $(".o-EmojiPicker-content .o-Emoji[data-index=0].bg-200").data( "codepoints" ); await afterNextRender(() => triggerHotkey("Enter")); @@ -87,13 +87,13 @@ QUnit.test("recent category (basic)", async (assert) => { const { openDiscuss } = await start(); await openDiscuss(channelId); await click("button[aria-label='Emojis']"); - assert.containsNone($, ".o-mail-EmojiPicker-navbar [title='Frequently used']"); - await click(".o-mail-EmojiPicker-content .o-mail-Emoji:contains(😀)"); + assert.containsNone($, ".o-EmojiPicker-navbar [title='Frequently used']"); + await click(".o-EmojiPicker-content .o-Emoji:contains(😀)"); await click("button[aria-label='Emojis']"); - assert.containsOnce($, ".o-mail-EmojiPicker-navbar [title='Frequently used']"); + assert.containsOnce($, ".o-EmojiPicker-navbar [title='Frequently used']"); assert.containsOnce( $, - "span:contains(Frequently used) ~ .o-mail-Emoji:contains(😀) ~ span:contains(Smileys & Emotion)" + "span:contains(Frequently used) ~ .o-Emoji:contains(😀) ~ span:contains(Smileys & Emotion)" ); }); @@ -103,23 +103,23 @@ QUnit.test("emoji usage amount orders frequent emojis", async (assert) => { const { openDiscuss } = await start(); await openDiscuss(channelId); await click("button[aria-label='Emojis']"); - await click(".o-mail-EmojiPicker-content .o-mail-Emoji:contains(😀)"); + await click(".o-EmojiPicker-content .o-Emoji:contains(😀)"); await click("button[aria-label='Emojis']"); - await click(".o-mail-EmojiPicker-content .o-mail-Emoji:contains(👽)"); + await click(".o-EmojiPicker-content .o-Emoji:contains(👽)"); await click("button[aria-label='Emojis']"); - await click(".o-mail-EmojiPicker-content .o-mail-Emoji:contains(👽)"); + await click(".o-EmojiPicker-content .o-Emoji:contains(👽)"); await click("button[aria-label='Emojis']"); assert.containsOnce( $, - "span:contains(Frequently used) ~ .o-mail-Emoji:contains(😀) ~ span:contains(Smileys & Emotion)" + "span:contains(Frequently used) ~ .o-Emoji:contains(😀) ~ span:contains(Smileys & Emotion)" ); assert.containsOnce( $, - "span:contains(Frequently used) ~ .o-mail-Emoji:contains(👽) ~ span:contains(Smileys & Emotion)" + "span:contains(Frequently used) ~ .o-Emoji:contains(👽) ~ span:contains(Smileys & Emotion)" ); assert.containsOnce( $, - "span:contains(Frequently used) ~ .o-mail-Emoji:contains(👽) ~ .o-mail-Emoji:contains(😀) ~ span:contains(Smileys & Emotion)" + "span:contains(Frequently used) ~ .o-Emoji:contains(👽) ~ .o-Emoji:contains(😀) ~ span:contains(Smileys & Emotion)" ); }); @@ -133,7 +133,7 @@ QUnit.test("posting :wink: in message should impact recent", async (assert) => { await click("button[aria-label='Emojis']"); assert.containsOnce( $, - "span:contains(Frequently used) ~ .o-mail-Emoji:contains(😉) ~ span:contains(Smileys & Emotion)" + "span:contains(Frequently used) ~ .o-Emoji:contains(😉) ~ span:contains(Smileys & Emotion)" ); }); @@ -148,7 +148,7 @@ QUnit.test("posting :snowman: in message should impact recent", async (assert) = await click("button[aria-label='Emojis']"); assert.containsOnce( $, - "span:contains(Frequently used) ~ .o-mail-Emoji:contains(☃️) ~ span:contains(Smileys & Emotion)" + "span:contains(Frequently used) ~ .o-Emoji:contains(☃️) ~ span:contains(Smileys & Emotion)" ); }); @@ -158,7 +158,7 @@ QUnit.test("first category should be highlight by default", async (assert) => { const { openDiscuss } = await start(); await openDiscuss(channelId); await click("button[aria-label='Emojis']"); - assert.containsOnce($, ".o-mail-EmojiPicker-navbar .o-mail-Emoji:eq(0).bg-300"); + assert.containsOnce($, ".o-EmojiPicker-navbar .o-Emoji:eq(0).bg-300"); }); QUnit.test( @@ -170,11 +170,11 @@ QUnit.test( await openDiscuss(channelId); await click("button[aria-label='Emojis']"); await afterNextRender(() => - $(".o-mail-EmojiPicker-content .o-mail-Emoji:contains(👺)")[0].dispatchEvent( + $(".o-EmojiPicker-content .o-Emoji:contains(👺)")[0].dispatchEvent( new MouseEvent("click", { shiftKey: true }) ) ); - assert.containsOnce($, ".o-mail-EmojiPicker"); + assert.containsOnce($, ".o-EmojiPicker"); assert.strictEqual($(".o-mail-Composer-input").val(), "👺"); } ); diff --git a/addons/mail/static/tests/message/message_tests.js b/addons/mail/static/tests/message/message_tests.js index 54f08496849..cbbeeea1b28 100644 --- a/addons/mail/static/tests/message/message_tests.js +++ b/addons/mail/static/tests/message/message_tests.js @@ -172,7 +172,7 @@ QUnit.test("Do not stop edition on click away when clicking on emoji", async (as await click(".o-mail-Message [title='Expand']"); await click(".o-mail-Message [title='Edit']"); await click(".o-mail-Composer button[aria-label='Emojis']"); - await click(".o-mail-EmojiPicker-content .o-mail-Emoji"); + await click(".o-EmojiPicker-content .o-Emoji"); assert.containsOnce($, ".o-mail-Message-editable .o-mail-Composer"); }); @@ -442,7 +442,7 @@ QUnit.test("Can open emoji picker after edit mode", async (assert) => { await click(".o-mail-Message [title='Edit']"); await triggerEvent(document.body, ".o-mail-DiscussSidebar", "click"); await click("[title='Add a Reaction']"); - assert.containsOnce($, ".o-mail-EmojiPicker"); + assert.containsOnce($, ".o-EmojiPicker"); }); QUnit.test("Can add a reaction", async (assert) => { @@ -460,7 +460,7 @@ QUnit.test("Can add a reaction", async (assert) => { const { openDiscuss } = await start(); await openDiscuss(channelId); await click("[title='Add a Reaction']"); - await click(".o-mail-Emoji:contains(😅)"); + await click(".o-Emoji:contains(😅)"); assert.containsOnce($, ".o-mail-MessageReaction:contains('😅')"); }); @@ -479,7 +479,7 @@ QUnit.test("Can remove a reaction", async (assert) => { const { openDiscuss } = await start(); await openDiscuss(channelId); await click("[title='Add a Reaction']"); - await click(".o-mail-Emoji:contains(😅)"); + await click(".o-Emoji:contains(😅)"); await click(".o-mail-MessageReaction"); assert.containsNone($, ".o-mail-MessageReaction:contains('😅')"); }); @@ -543,7 +543,7 @@ QUnit.test("Reaction summary", async (assert) => { const partnerId = pyEnv["res.partner"].create({ name }); pyEnv.currentPartnerId = partnerId; await click("[title='Add a Reaction']"); - await click(".o-mail-Emoji:contains(😅)"); + await click(".o-Emoji:contains(😅)"); assert.hasAttrValue($(".o-mail-MessageReaction")[0], "title", expectedSummaries[idx]); } }); @@ -563,9 +563,9 @@ QUnit.test("Add the same reaction twice from the emoji picker", async (assert) = const { openDiscuss } = await start(); await openDiscuss(channelId); await click("[title='Add a Reaction']"); - await click(".o-mail-Emoji:contains(😅)"); + await click(".o-Emoji:contains(😅)"); await click("[title='Add a Reaction']"); - await click(".o-mail-Emoji:contains(😅)"); + await click(".o-Emoji:contains(😅)"); assert.containsOnce($, ".o-mail-MessageReaction:contains('😅')"); }); diff --git a/addons/mail/static/tests/web/fields/emojis_fields_tests.js b/addons/mail/static/tests/web/fields/emojis_fields_tests.js index 47ee5cbe76a..d43458af363 100644 --- a/addons/mail/static/tests/web/fields/emojis_fields_tests.js +++ b/addons/mail/static/tests/web/fields/emojis_fields_tests.js @@ -29,9 +29,9 @@ export async function testEmojiButtonVisible(assert, selector) { export async function testEmojiButton(assert, input, button) { // emoji picker opens await click(button); - assert.containsOnce($, ".o-mail-EmojiPicker"); + assert.containsOnce($, ".o-EmojiPicker"); // clicking an emoji adds it to the input field - const emoji_1 = $(".o-mail-EmojiPicker-content .o-mail-Emoji")[0]; + const emoji_1 = $(".o-EmojiPicker-content .o-Emoji")[0]; const emojiChar_1 = emoji_1.textContent; await click(emoji_1); assert.ok(input.value.endsWith(emojiChar_1)); @@ -40,7 +40,7 @@ export async function testEmojiButton(assert, input, button) { input.setSelectionRange(2, input.value.length - emojiChar_1.length); // pick an emoji while the text is selected await click(button); - const emoji_2 = $(".o-mail-EmojiPicker-content .o-mail-Emoji")[0]; + const emoji_2 = $(".o-EmojiPicker-content .o-Emoji")[0]; const emojiChar_2 = emoji_2.textContent; await click(emoji_2); // the selected region is replaced and the rest stays in place diff --git a/addons/point_of_sale/__manifest__.py b/addons/point_of_sale/__manifest__.py index 46ca8ef8b80..c92c9397252 100644 --- a/addons/point_of_sale/__manifest__.py +++ b/addons/point_of_sale/__manifest__.py @@ -92,6 +92,7 @@ 'web/static/src/env.js', 'web/static/src/core/utils/transitions.scss', # needs to be loaded before other scss files 'web/static/src/core/**/*', + ('remove', 'web/static/src/core/emoji_picker/emoji_data.js'), ('remove', 'web/static/src/core/errors/error_handlers.js'), # error handling in PoS is different from the webclient # formatMonetary 'web/static/src/views/fields/formatters.js', diff --git a/addons/pos_self_order/__manifest__.py b/addons/pos_self_order/__manifest__.py index 360902e0fdb..c79b8b5edc3 100644 --- a/addons/pos_self_order/__manifest__.py +++ b/addons/pos_self_order/__manifest__.py @@ -36,6 +36,7 @@ "web/static/src/session.js", "web/static/src/core/utils/transitions.scss", "web/static/src/core/**/*", + ('remove', 'web/static/src/core/emoji_picker/emoji_data.js'), ("remove", "web/static/src/core/browser/router_service.js"), ("remove", "web/static/src/core/debug/**/*"), "web/static/lib/owl/owl.js", diff --git a/addons/project/__manifest__.py b/addons/project/__manifest__.py index e188b4be49a..ea6d9ca9248 100644 --- a/addons/project/__manifest__.py +++ b/addons/project/__manifest__.py @@ -122,6 +122,7 @@ ('remove', 'web/static/src/webclient/navbar/navbar.scss'), # already in assets_common ('remove', 'web/static/src/webclient/clickbot/clickbot.js'), # lazy loaded ('remove', 'web/static/src/views/form/button_box/*.scss'), + ('remove', 'web/static/src/core/emoji_picker/emoji_data.js'), # remove the report code and whitelist only what's needed ('remove', 'web/static/src/webclient/actions/reports/**/*'), diff --git a/addons/web/__manifest__.py b/addons/web/__manifest__.py index 48e07bab124..9e2d38335c5 100644 --- a/addons/web/__manifest__.py +++ b/addons/web/__manifest__.py @@ -163,6 +163,9 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/xml/colorpicker.xml', 'web/static/src/legacy/translations_loaded.js', ], + 'web.assets_emoji': [ + 'web/static/src/core/emoji_picker/emoji_data.js' + ], 'web.assets_backend': [ ('include', 'web._assets_helpers'), ('include', 'web._assets_backend_helpers'), @@ -184,6 +187,7 @@ This module provides the core of the Odoo Web Client. ('remove', 'web/static/src/webclient/clickbot/clickbot.js'), # lazy loaded ('remove', 'web/static/src/views/form/button_box/*.scss'), ('remove', 'web/static/src/core/file_viewer/file_viewer.dark.scss'), + ('remove', 'web/static/src/core/emoji_picker/emoji_data.js'), # remove the report code and whitelist only what's needed ('remove', 'web/static/src/webclient/actions/reports/**/*'), @@ -397,6 +401,7 @@ This module provides the core of the Odoo Web Client. ('remove', 'web/static/src/core/commands/**/*'), ('remove', 'web/static/src/core/debug/debug_menu.js'), ('remove', 'web/static/src/core/file_viewer/file_viewer.dark.scss'), + ('remove', 'web/static/src/core/emoji_picker/emoji_data.js'), 'web/static/src/public/error_notifications.js', 'web/static/src/legacy/utils.js', diff --git a/addons/web/static/src/core/emoji_picker/emoji_picker.js b/addons/web/static/src/core/emoji_picker/emoji_picker.js index a7e5e00a32d..fd527ca8555 100644 --- a/addons/web/static/src/core/emoji_picker/emoji_picker.js +++ b/addons/web/static/src/core/emoji_picker/emoji_picker.js @@ -1,6 +1,6 @@ /* @odoo-module */ -import { markEventHandled } from "@mail/utils/common/misc"; +import { markEventHandled } from "@web/core/utils/misc"; import { Component, @@ -106,7 +106,7 @@ export function useEmojiPicker(ref, props, options = {}) { } export const loader = { - loadEmoji: memoize(() => getBundle("mail.assets_emoji").then(loadBundle)), + loadEmoji: memoize(() => getBundle("web.assets_emoji").then(loadBundle)), }; /** @@ -126,7 +126,7 @@ export const EMOJI_PER_ROW = 9; export class EmojiPicker extends Component { static props = ["onSelect", "className?", "close?", "onClose?", "storeScroll?"]; - static template = "mail.EmojiPicker"; + static template = "web.EmojiPicker"; recent = []; categories = null; @@ -150,7 +150,7 @@ export class EmojiPicker extends Component { this.emojis.map((emoji) => [emoji.codepoints, emoji]) ); this.state.categoryId = this.categories[0]?.sortId; - this.recent = JSON.parse(browser.localStorage.getItem("mail.emoji.frequent") || "{}"); + this.recent = JSON.parse(browser.localStorage.getItem("web.emoji.frequent") || "{}"); this.recentCategory = { name: "Frequently used", displayName: _t("Frequently used"), @@ -176,7 +176,7 @@ export class EmojiPicker extends Component { this.shouldScrollElem = false; const getElement = () => this.gridRef.el.querySelector( - `.o-mail-EmojiPicker-category[data-category="${this.state.categoryId}"` + `.o-EmojiPicker-category[data-category="${this.state.categoryId}"` ); const elem = getElement(); if (elem) { @@ -255,9 +255,10 @@ export class EmojiPicker extends Component { break; } case "Enter": + ev.preventDefault(); this.gridRef.el .querySelector( - `.o-mail-EmojiPicker-content .o-mail-Emoji[data-index="${this.state.activeEmojiIndex}"]` + `.o-EmojiPicker-content .o-Emoji[data-index="${this.state.activeEmojiIndex}"]` ) .click(); break; @@ -290,7 +291,7 @@ export class EmojiPicker extends Component { this.props.onSelect(codepoints); this.recent[codepoints] ??= 0; this.recent[codepoints]++; - browser.localStorage.setItem("mail.emoji.frequent", JSON.stringify(this.recent)); + browser.localStorage.setItem("web.emoji.frequent", JSON.stringify(this.recent)); this.gridRef.el.scrollTop = 0; if (!ev.shiftKey) { this.props.close?.(); diff --git a/addons/web/static/src/core/emoji_picker/emoji_picker.scss b/addons/web/static/src/core/emoji_picker/emoji_picker.scss index fef6a8176c4..4aeb7a5a812 100644 --- a/addons/web/static/src/core/emoji_picker/emoji_picker.scss +++ b/addons/web/static/src/core/emoji_picker/emoji_picker.scss @@ -1,9 +1,9 @@ -.popover .o-mail-EmojiPicker { +.popover .o-EmojiPicker { width: 285px; height: 350px; } -.o-mail-EmojiPicker { +.o-EmojiPicker { display: grid; grid-template-areas: @@ -15,7 +15,7 @@ grid-template-rows: 40px 1fr 32px; } - .o-mail-Emoji { + .o-Emoji { width: 30px; font-size: 1rem; &:hover { @@ -23,27 +23,27 @@ } } - &.o-small .o-mail-Emoji { + &.o-small .o-Emoji { width: 40px; font-size: 1.5rem; } - .o-mail-EmojiPicker-content { + .o-EmojiPicker-content { grid-area: content; } - .o-mail-EmojiPicker-navbar { + .o-EmojiPicker-navbar { grid-area: navbar; - .o-mail-Emoji { + .o-Emoji { filter: grayscale(1); } } - .o-mail-EmojiPicker-sectionIcon { + .o-EmojiPicker-sectionIcon { filter: grayscale(1); } - .o-mail-EmojiPicker-search { + .o-EmojiPicker-search { grid-area: search; background-color: $input-bg; @@ -59,13 +59,13 @@ } } - .o-mail-EmojiPicker-empty { + .o-EmojiPicker-empty { font-size: 5rem !important; filter: grayscale(0.25); } } -.o-mail-EmojiPicker-category:before { +.o-EmojiPicker-category:before { // invisible character so that category has constant height, regardless of text content. content: "\200b"; /* unicode zero width space character */ } diff --git a/addons/web/static/src/core/emoji_picker/emoji_picker.xml b/addons/web/static/src/core/emoji_picker/emoji_picker.xml index 5f6885de8bf..b13f432f40b 100644 --- a/addons/web/static/src/core/emoji_picker/emoji_picker.xml +++ b/addons/web/static/src/core/emoji_picker/emoji_picker.xml @@ -1,29 +1,29 @@ - -
+ +
- 😵‍💫 + 😵‍💫 Failed to load emojis... -