[MOV] mail, social: move emojis mixin and field from social to mail
PURPOSE Allow to use the text field with integrated emojis support in other apps notably mass mailing SPECIFICATIONS Move emojis mixin and FieldTextEmojis field definition from social to mail application. Rename emojis namespace accordingly. LINKS Task #2117238 PR #40045 Enterprise PR odoo/enterprise#6630
This commit is contained in:
committed by
Thibault Delavallée
parent
0ddfbab90f
commit
e870ccf9ab
@@ -62,5 +62,6 @@
|
||||
'static/src/xml/announcement.xml',
|
||||
'static/src/xml/user_menu.xml',
|
||||
'static/src/xml/web_kanban_activity.xml',
|
||||
'static/src/xml/text_emojis.xml',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
odoo.define('mail.emoji_mixin', function (require) {
|
||||
"use strict";
|
||||
|
||||
var dom = require('web.dom');
|
||||
var emojis = require('mail.emojis');
|
||||
|
||||
/**
|
||||
* This mixin gathers a few methods that are used to handle emojis.
|
||||
*
|
||||
* It's used to:
|
||||
*
|
||||
* - handle the click on an emoji from a dropdown panel and add it to the related textarea
|
||||
* - replace the emoji code with the actual character, ex: ' :) ' -> ' 😊 '
|
||||
* - format text and wrap the emojis around <span class="o_mail_emoji"> to make them look nicer
|
||||
*
|
||||
* Methods are based on the collections of emojis available in mail.emojis
|
||||
*
|
||||
*/
|
||||
return {
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* This method should be bound to a click event on an emoji.
|
||||
* (used in textarea's emojis dropdown list)
|
||||
*
|
||||
* It assumes that a '_getTargetTextArea' method is defined that will return the related
|
||||
* $textarea element.
|
||||
*
|
||||
* Some code from 'basic_composer.js' was copy/pasted to handle the cursor position in the
|
||||
* textarea and handle text replacement if text was selected by the user.
|
||||
*
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onEmojiClick: function (ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
var $target = $(ev.currentTarget);
|
||||
var $textarea = this._getTargetTextArea($target);
|
||||
|
||||
// copy/pasted from 'basic_composer.js'
|
||||
var inputElement = $textarea.get(0);
|
||||
var cursorPosition = inputElement ? dom.getSelectionRange(inputElement) : {start: 0, end: 0};
|
||||
var inputVal = $textarea.val();
|
||||
var leftSubstring = inputVal.substring(0, cursorPosition.start);
|
||||
var rightSubstring = inputVal.substring(cursorPosition.end);
|
||||
var newInputVal = [leftSubstring , $target.text().trim(), rightSubstring].join("");
|
||||
var newCursorPosition = newInputVal.length - rightSubstring.length;
|
||||
$textarea.val(newInputVal);
|
||||
$textarea.focus();
|
||||
$textarea[0].setSelectionRange(newCursorPosition, newCursorPosition);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* This method is used to wrap emojis in a text message with <span class="o_mail_emoji">
|
||||
* As this returns html to be used in a 't-raw' argument, it first makes sure that the
|
||||
* passed text message is html escaped for safety reasons.
|
||||
*
|
||||
* @param {String} message a text message to format
|
||||
*/
|
||||
_formatText: function (message) {
|
||||
message = this._htmlEscape(message);
|
||||
message = this._wrapEmojis(message);
|
||||
message = message.replace(/(?:\r\n|\r|\n)/g, '<br>');
|
||||
|
||||
return message;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adapted from qweb2.js#html_escape to avoid formatting '&'
|
||||
*
|
||||
* @param {String} s
|
||||
* @private
|
||||
*/
|
||||
_htmlEscape: function (s) {
|
||||
if (s == null) {
|
||||
return '';
|
||||
}
|
||||
return String(s).replace(/</g, '<').replace(/>/g, '>');
|
||||
},
|
||||
|
||||
/**
|
||||
* Will use the mail.emojis library to wrap emojis unicode around a span with a special font
|
||||
* that will make them look nicer (colored, ...).
|
||||
*
|
||||
* @param {String} message
|
||||
*/
|
||||
_wrapEmojis: function (message) {
|
||||
emojis.forEach(function (emoji) {
|
||||
message = message.replace(
|
||||
new RegExp(emoji.unicode.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'),
|
||||
'<span class="o_mail_emoji">' + emoji.unicode + '</span>'
|
||||
);
|
||||
});
|
||||
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,129 @@
|
||||
odoo.define('mail.text_emojis_field', function (require) {
|
||||
"use strict";
|
||||
|
||||
var basicFields = require('web.basic_fields');
|
||||
var core = require('web.core');
|
||||
var emojis = require('mail.emojis');
|
||||
var registry = require('web.field_registry');
|
||||
var MailEmojisMixin = require('mail.emoji_mixin');
|
||||
var _onEmojiClickMixin = MailEmojisMixin._onEmojiClick;
|
||||
var QWeb = core.qweb;
|
||||
|
||||
var FieldTextEmojis = basicFields.FieldText.extend(MailEmojisMixin, {
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._updatePreview =_.throttle(this._updatePreview, 1000, {leading: false});
|
||||
this.emojis = emojis;
|
||||
},
|
||||
|
||||
/**
|
||||
* This will add an emoji button that shows the emojis selection dropdown.
|
||||
*
|
||||
* We use 'on_attach_callback' because we need the element to be attached to the form first.
|
||||
* That's because the $emojisIcon element needs to be rendered outside of this $el
|
||||
* (which is an textarea, that can't 'contain' any other elements).
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
on_attach_callback: function () {
|
||||
var self = this;
|
||||
|
||||
if (!this.$emojisIcon) {
|
||||
this.$emojisIcon = $(QWeb.render(
|
||||
'mail.EmojisDropdown', {
|
||||
widget: this,
|
||||
emojisDropdownId: 'emojis_dropdown'
|
||||
}
|
||||
));
|
||||
this.$emojisIcon.find('.o_mail_emoji').on('click', function (ev) {
|
||||
self._onEmojiClick(ev);
|
||||
self._isDirty = true;
|
||||
});
|
||||
this.$el.after(this.$emojisIcon);
|
||||
}
|
||||
|
||||
if (this.mode === 'edit') {
|
||||
this.$emojisIcon.show();
|
||||
} else {
|
||||
this.$emojisIcon.hide();
|
||||
}
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_render: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
if (this.mode !== 'edit') {
|
||||
this.$el.html(this._formatText(this.$el.text()));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Overridden because we need to add the Emoji to the input AND trigger
|
||||
* the 'change' event to refresh the various post previews.
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_onEmojiClick: function () {
|
||||
_onEmojiClickMixin.apply(this, arguments);
|
||||
this.$input.trigger('change');
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* By default, the 'change' event is only triggered when the textarea is blurred.
|
||||
*
|
||||
* We override this method because we want to update the various post previews while
|
||||
* the user is typing his message (and not only on blur).
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_onKeydown: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._updatePreview();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used by MailEmojisMixin, check its document for more info.
|
||||
*
|
||||
* @param {$.Element} $emoji
|
||||
* @private
|
||||
*/
|
||||
_getTargetTextArea($emoji) {
|
||||
return this.$el;
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggers the 'change' event to refresh the various post previews.
|
||||
* This method is throttled to run at most once every second.
|
||||
* (to avoid spamming the server while the user is typing his message)
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_updatePreview: function () {
|
||||
this.$input.trigger('change');
|
||||
}
|
||||
};
|
||||
|
||||
registry.add('text_emojis', FieldTextEmojis);
|
||||
|
||||
return FieldTextEmojis;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
.o_mail_add_emoji {
|
||||
float: right;
|
||||
margin-bottom: 1rem;
|
||||
.dropdown-menu {
|
||||
.o_mail_emoji {
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
|
||||
@include hover-focus() {
|
||||
background-color: grey('100');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_mail_emojis_dropdown {
|
||||
height: 35px;
|
||||
width: 40px;
|
||||
float: right;
|
||||
margin-top: -35px;
|
||||
|
||||
.dropdown-toggle:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<div t-name="mail.EmojisDropdown" t-attf-class="o_mail_emojis_dropdown o_mail_add_emoji dropdown">
|
||||
<button class="btn btn-block dropdown-toggle px-3 py-1"
|
||||
type="button" t-att-id="emojisDropdownId"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
title="Add an emoji">
|
||||
<i class="fa fa-smile-o"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right border-0 p-2" t-att-aria-labelledby="emojisDropdownId" style="width: 406px">
|
||||
<t t-call="mail.Composer.emojis">
|
||||
<t t-set="emojis" t-value="widget.emojis"></t>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</templates>
|
||||
@@ -81,9 +81,12 @@
|
||||
<script type="text/javascript" src="/mail/static/src/js/views/activity/activity_record.js"></script>
|
||||
<!-- Attachment box -->
|
||||
<script type="text/javascript" src="/mail/static/src/js/attachment_box.js"></script>
|
||||
<!-- Others -->
|
||||
<!-- Emojis -->
|
||||
<script type="text/javascript" src="/mail/static/src/js/emojis.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/mail/static/src/js/emojis_mixin.js"/>
|
||||
<script type="text/javascript" src="/mail/static/src/js/text_emojis_field.js"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/mail/static/src/scss/emojis.scss"/>
|
||||
<!-- Others -->
|
||||
<link rel="stylesheet" type="text/scss" href="/mail/static/src/scss/announcement.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/mail/static/src/scss/discuss.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/mail/static/src/scss/abstract_thread_window.scss"/>
|
||||
|
||||
Reference in New Issue
Block a user