[REVERT] "[IMP] mail : display mentions in green color in edit mode"
This reverts commit c778de44c7.
This commit is contained in:
committed by
Géry Debongnie
parent
c52243794d
commit
93f9131450
@@ -184,7 +184,7 @@ var Chatter = Widget.extend({
|
||||
input_baseline: 14,
|
||||
is_log: options && options.is_log,
|
||||
record_name: this.record_name,
|
||||
default_body: old_composer && old_composer.$input && old_composer.$input.html(),
|
||||
default_body: old_composer && old_composer.$input && old_composer.$input.val(),
|
||||
default_mention_selections: old_composer && old_composer.mention_get_listener_selections(),
|
||||
});
|
||||
this.composer.on('input_focused', this, function () {
|
||||
|
||||
@@ -211,7 +211,7 @@ var ChatterComposer = composer.BasicComposer.extend({
|
||||
recipient_done.then(function (partner_ids) {
|
||||
var context = {
|
||||
default_parent_id: self.id,
|
||||
default_body: utils.get_text2html(self.$input.html()),
|
||||
default_body: utils.get_text2html(self.$input.val()),
|
||||
default_attachment_ids: _.pluck(self.get('attachment_ids'), 'id'),
|
||||
default_partner_ids: partner_ids,
|
||||
default_is_log: self.options.is_log,
|
||||
|
||||
@@ -7,6 +7,7 @@ var utils = require('mail.utils');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var data = require('web.data');
|
||||
var dom = require('web.dom');
|
||||
var session = require('web.session');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
@@ -29,7 +30,7 @@ var MentionManager = Widget.extend({
|
||||
|
||||
events: {
|
||||
"mouseover .o_mention_proposition": "on_hover_mention_proposition",
|
||||
"click .o_mention_proposition": "_onClickMentionItem",
|
||||
"click .o_mention_proposition": "on_click_mention_item",
|
||||
},
|
||||
|
||||
init: function (parent, options) {
|
||||
@@ -52,6 +53,49 @@ var MentionManager = Widget.extend({
|
||||
this.$('.o_mention_proposition').removeClass('active');
|
||||
$elem.addClass('active');
|
||||
},
|
||||
on_click_mention_item: function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
var text_input = this.composer.$input.val();
|
||||
var id = $(event.currentTarget).data('id');
|
||||
var selected_suggestion = _.find(_.flatten(this.get('mention_suggestions')), function (s) {
|
||||
return s.id === id;
|
||||
});
|
||||
var substitution = selected_suggestion.substitution;
|
||||
if (!substitution) { // no substitution string given, so use the mention name instead
|
||||
// replace white spaces with non-breaking spaces to facilitate mentions detection in text
|
||||
selected_suggestion.name = selected_suggestion.name.replace(/ /g, NON_BREAKING_SPACE);
|
||||
substitution = this.active_listener.delimiter + selected_suggestion.name;
|
||||
}
|
||||
var get_mention_index = function (matches, cursor_position) {
|
||||
for (var i=0; i<matches.length; i++) {
|
||||
if (cursor_position <= matches[i].index) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
// add the selected suggestion to the list
|
||||
if (this.active_listener.selection.length) {
|
||||
// get mention matches (ordered by index in the text)
|
||||
var matches = this._get_match(text_input, this.active_listener);
|
||||
var index = get_mention_index(matches, this._get_selection_positions().start);
|
||||
this.active_listener.selection.splice(index, 0, selected_suggestion);
|
||||
} else {
|
||||
this.active_listener.selection.push(selected_suggestion);
|
||||
}
|
||||
|
||||
// update input text, and reset dropdown
|
||||
var cursor_position = this._get_selection_positions().start;
|
||||
var text_left = text_input.substring(0, cursor_position-(this.mention_word.length+1));
|
||||
var text_right = text_input.substring(cursor_position, text_input.length);
|
||||
var text_input_new = text_left + substitution + ' ' + text_right;
|
||||
this.composer.$input.val(text_input_new);
|
||||
this._set_cursor_position(text_left.length+substitution.length+2);
|
||||
this.set('mention_suggestions', []);
|
||||
this.composer.focus(); // to trigger autoresize
|
||||
},
|
||||
|
||||
// Public API
|
||||
/**
|
||||
@@ -86,8 +130,8 @@ var MentionManager = Widget.extend({
|
||||
get_listener_selection: function (delimiter) {
|
||||
var listener = _.findWhere(this.listeners, {delimiter: delimiter});
|
||||
if (listener) {
|
||||
var inputMentions = this.composer.$input.text().match(new RegExp(delimiter+'[^ ]+(?= | )', 'g'));
|
||||
return this._validate_selection(listener.selection, inputMentions);
|
||||
var input_mentions = this.composer.$input.val().match(new RegExp(delimiter+'[^ ]+(?= | )', 'g'));
|
||||
return this._validate_selection(listener.selection, input_mentions);
|
||||
}
|
||||
return [];
|
||||
},
|
||||
@@ -106,15 +150,15 @@ var MentionManager = Widget.extend({
|
||||
*/
|
||||
detect_delimiter: function () {
|
||||
var self = this;
|
||||
var textVal = this.composer.$input.html();
|
||||
var cursorPosition = this._getSelectionPositions();
|
||||
var leftString = textVal.substring(0, cursorPosition);
|
||||
var text_val = this.composer.$input.val();
|
||||
var cursor_position = this._get_selection_positions().start;
|
||||
var left_string = text_val.substring(0, cursor_position);
|
||||
function validate_keyword (delimiter, beginning_only) {
|
||||
var delimiter_position = leftString.lastIndexOf(delimiter) - 1;
|
||||
var delimiter_position = left_string.lastIndexOf(delimiter) - 1;
|
||||
if (beginning_only && delimiter_position > 0) {
|
||||
return false;
|
||||
}
|
||||
var search_str = textVal.substring(delimiter_position, cursorPosition);
|
||||
var search_str = text_val.substring(delimiter_position, cursor_position);
|
||||
var pattern = "(^"+delimiter+"|(^\\s"+delimiter+"))";
|
||||
var regex_start = new RegExp(pattern, "g");
|
||||
search_str = search_str.replace(/^\s\s*|^[\n\r]/g, '');
|
||||
@@ -203,8 +247,6 @@ var MentionManager = Widget.extend({
|
||||
|
||||
/**
|
||||
* Returns the matches (as RexExp.exec does) for the mention in the input text
|
||||
*
|
||||
* @private
|
||||
* @param {String} input_text: the text to search matches
|
||||
* @param {Object} listener: the listener for which we want to find a match
|
||||
* @returns {Object[]} matches in the same format as RexExp.exec()
|
||||
@@ -228,32 +270,6 @@ var MentionManager = Widget.extend({
|
||||
}
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* Get cursor position in contenteditable div with it's inner html content.
|
||||
*
|
||||
* @private
|
||||
* @return a current cursor position
|
||||
*/
|
||||
_getSelectionPositions: function () {
|
||||
if (window.getSelection && window.getSelection().getRangeAt) {
|
||||
var range = window.getSelection().getRangeAt(0);
|
||||
var selectedObj = window.getSelection();
|
||||
var rangeCount = 0;
|
||||
var childNodes = selectedObj.anchorNode.parentNode.childNodes;
|
||||
for (var i = 0; i < childNodes.length; i++) {
|
||||
if (childNodes[i] === selectedObj.anchorNode) {
|
||||
break;
|
||||
}
|
||||
if (childNodes[i].outerHTML)
|
||||
rangeCount += childNodes[i].outerHTML.length;
|
||||
else if (childNodes[i].nodeType === 3) {
|
||||
rangeCount += childNodes[i].textContent.length;
|
||||
}
|
||||
}
|
||||
return range.startOffset + rangeCount;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {integer} keycode
|
||||
@@ -312,23 +328,9 @@ var MentionManager = Widget.extend({
|
||||
this.open = false;
|
||||
}
|
||||
},
|
||||
/*
|
||||
* Set cursor position after mention is added.
|
||||
* @private
|
||||
* @paran {int} node - id of childnode to set cursor position after it.
|
||||
*/
|
||||
_setCursorPosition: function (nodeID) {
|
||||
var range = document.createRange();
|
||||
var selection = window.getSelection();
|
||||
range.setStart(document.getElementById(nodeID).nextSibling, 1);
|
||||
range.collapse(true);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
this.composer.$input.focus();
|
||||
},
|
||||
_validate_selection: function (selection, inputMentions) {
|
||||
_validate_selection: function (selection, input_mentions) {
|
||||
var validated_selection = [];
|
||||
_.each(inputMentions, function (mention) {
|
||||
_.each(input_mentions, function (mention) {
|
||||
var validated_mention = _.findWhere(selection, {name: mention.slice(1)});
|
||||
if (validated_mention) {
|
||||
validated_selection.push(validated_mention);
|
||||
@@ -337,60 +339,15 @@ var MentionManager = Widget.extend({
|
||||
return validated_selection;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* after click on mention item it set to a contenteditable div with it's new
|
||||
* cursor postion
|
||||
*
|
||||
* @private
|
||||
* @param {KeyboardEvent} event - to get a current selected mention from
|
||||
* list and eventlistener.
|
||||
*/
|
||||
_onClickMentionItem: function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
var textInput = this.composer.$input.html();
|
||||
var id = $(event.currentTarget).data('id');
|
||||
var selectedSuggestion = _.find(_.flatten(this.get('mention_suggestions')), function (s) {
|
||||
return s.id === id;
|
||||
// Cursor position and selection utils
|
||||
_get_selection_positions: function () {
|
||||
var el = this.composer.$input.get(0);
|
||||
return el ? dom.getSelectionRange(el) : {start: 0, end: 0};
|
||||
},
|
||||
_set_cursor_position: function (pos) {
|
||||
this.composer.$input.each(function (index, elem) {
|
||||
dom.setSelectionRange(elem, {start: pos, end: pos});
|
||||
});
|
||||
var substitution = selectedSuggestion.substitution;
|
||||
if (!substitution) { // no substitution string given, so use the mention name instead
|
||||
// replace white spaces with non-breaking spaces to facilitate mentions detection in text
|
||||
selectedSuggestion.name = selectedSuggestion.name.replace(/ /g, NON_BREAKING_SPACE);
|
||||
substitution = _.escape(this.active_listener.delimiter + selectedSuggestion.name);
|
||||
}
|
||||
var getMentionIndex = function (matches, cursorPosition) {
|
||||
for (var i=0; i<matches.length; i++) {
|
||||
if (cursorPosition <= matches[i].index) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
// add the selected suggestion to the list
|
||||
if (this.active_listener.selection.length) {
|
||||
// get mention matches (ordered by index in the text)
|
||||
var matches = this._get_match(textInput, this.active_listener);
|
||||
var index = getMentionIndex(matches, this._getSelectionPositions());
|
||||
this.active_listener.selection.splice(index, 0, selectedSuggestion);
|
||||
} else {
|
||||
this.active_listener.selection.push(selectedSuggestion);
|
||||
}
|
||||
|
||||
// update input text, and reset dropdown
|
||||
var cursorPosition = this._getSelectionPositions();
|
||||
var textLeft = textInput.substring(0, cursorPosition-(this.mention_word.length+1));
|
||||
var textRight = textInput.substring(cursorPosition, textInput.length);
|
||||
var nodeID = _.uniqueId('node');
|
||||
var newTextInput = textLeft + "<a id="+ nodeID+">" + substitution + "</a> " + textRight;
|
||||
this.composer.$input.html(newTextInput);
|
||||
this._setCursorPosition(nodeID);
|
||||
this.set('mention_suggestions', []);
|
||||
},
|
||||
|
||||
});
|
||||
@@ -398,8 +355,8 @@ var MentionManager = Widget.extend({
|
||||
var BasicComposer = Widget.extend({
|
||||
template: "mail.ChatComposer",
|
||||
events: {
|
||||
"keydown .o_composer_input": "_onKeydown",
|
||||
"keyup .o_composer_input": "_onKeyup",
|
||||
"keydown .o_composer_input textarea": "on_keydown",
|
||||
"keyup .o_composer_input": "on_keyup",
|
||||
"change input.o_input_file": "on_attachment_change",
|
||||
"click .o_composer_button_send": "send_message",
|
||||
"click .o_composer_button_add_attachment": "on_click_add_attachment",
|
||||
@@ -410,7 +367,6 @@ var BasicComposer = Widget.extend({
|
||||
'focusout .o_composer_button_emoji': '_onEmojiButtonFocusout',
|
||||
'focus .o_mail_emoji_container .o_mail_emoji': '_onEmojiImageFocus',
|
||||
'click .o_mail_emoji_container .o_mail_emoji': '_onEmojiImageClick',
|
||||
'input .o_composer_input': '_removeMention',
|
||||
},
|
||||
// RPCs done to fetch the mention suggestions are throttled with the following value
|
||||
MENTION_THROTTLE: 200,
|
||||
@@ -487,12 +443,12 @@ var BasicComposer = Widget.extend({
|
||||
|
||||
this.$attachment_button = this.$(".o_composer_button_add_attachment");
|
||||
this.$attachments_list = this.$('.o_composer_attachments_list');
|
||||
this.$input = this.$('.o_composer_input');
|
||||
this.$input = this.$('.o_composer_input textarea');
|
||||
this.$input.focus(function () {
|
||||
self.trigger('input_focused');
|
||||
});
|
||||
this.$input.html(this.options.default_body);
|
||||
this.$input.css('min-height', this.options.input_min_height);
|
||||
this.$input.val(this.options.default_body);
|
||||
dom.autoresize(this.$input, {parent: this, min_height: this.options.input_min_height});
|
||||
|
||||
// Attachments
|
||||
$(window).on(this.fileupload_id, this.on_attachment_loaded);
|
||||
@@ -514,7 +470,7 @@ var BasicComposer = Widget.extend({
|
||||
// behavior for the chatter composer
|
||||
|
||||
//Removing unwanted extra spaces from message
|
||||
var value = _.escape(this.$input.text()).trim();
|
||||
var value = _.escape(this.$input.val()).trim();
|
||||
value = value.replace(/(\r|\n){2,}/g, '<br/><br/>');
|
||||
value = value.replace(/(\r|\n)/g, '<br/>');
|
||||
|
||||
@@ -545,7 +501,7 @@ var BasicComposer = Widget.extend({
|
||||
|
||||
clear_composer: function() {
|
||||
// Empty input, selected partners and attachments
|
||||
this.$input.html('');
|
||||
this.$input.val('');
|
||||
this.mention_manager.reset_selections();
|
||||
this.set('attachment_ids', []);
|
||||
},
|
||||
@@ -557,7 +513,7 @@ var BasicComposer = Widget.extend({
|
||||
getState: function () {
|
||||
return {
|
||||
attachments: this.get('attachment_ids'),
|
||||
text: this.$input.html(),
|
||||
text: this.$input.val(),
|
||||
};
|
||||
},
|
||||
|
||||
@@ -572,7 +528,7 @@ var BasicComposer = Widget.extend({
|
||||
|
||||
setState: function (state) {
|
||||
this.set('attachment_ids', state.attachments);
|
||||
this.$input.html(state.text);
|
||||
this.$input.val(state.text);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -581,18 +537,8 @@ var BasicComposer = Widget.extend({
|
||||
should_send: function (event) {
|
||||
return !event.shiftKey;
|
||||
},
|
||||
/**
|
||||
* _onKeydown event is triggered when is key is pressed
|
||||
* - on UP and DOWN arrow is pressed then event prevents it's default
|
||||
* behaviour if mention manager is open else it break it.
|
||||
* - on ENTER key is pressed and mention manager is open then event
|
||||
* prevents it's default behaviour else check if ControlKey is
|
||||
* pressed or not if yes then it send a message
|
||||
*
|
||||
* @private
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
_onKeydown: function (event) {
|
||||
|
||||
on_keydown: function (event) {
|
||||
switch(event.which) {
|
||||
// UP, DOWN, TAB: prevent moving cursor if navigation in mention propositions
|
||||
case $.ui.keyCode.UP:
|
||||
@@ -617,16 +563,7 @@ var BasicComposer = Widget.extend({
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* _onKeyup event is triggered when key is released.
|
||||
* on ESCAP key it close the mention suggestion dropdown menu.
|
||||
* on ENTER key it selects that mention and add to a content editable div.
|
||||
* on UP or DOWN key it highlights previous or next mention respectively.
|
||||
*
|
||||
* @private
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
_onKeyup: function (event) {
|
||||
on_keyup: function (event) {
|
||||
switch(event.which) {
|
||||
// ESCAPED KEYS: do nothing
|
||||
case $.ui.keyCode.END:
|
||||
@@ -743,13 +680,6 @@ var BasicComposer = Widget.extend({
|
||||
attachments: this.get('attachment_ids'),
|
||||
}));
|
||||
},
|
||||
// remove mention when user try to edit or remove it.
|
||||
_removeMention: function (event) {
|
||||
var anchorNode = window.getSelection().anchorNode;
|
||||
if (anchorNode.textContent.startsWith('@') && anchorNode.parentNode.tagName === 'A') {
|
||||
anchorNode.parentNode.remove();
|
||||
}
|
||||
},
|
||||
|
||||
// Mention
|
||||
mention_fetch_throttled: function (model, method, kwargs) {
|
||||
@@ -835,14 +765,8 @@ var BasicComposer = Widget.extend({
|
||||
},
|
||||
|
||||
// Others
|
||||
/**
|
||||
* used to check if contenteditable div is empty or not
|
||||
*
|
||||
* @return - it returns length of attachment and trim content of input box
|
||||
* tp check for empty.
|
||||
*/
|
||||
is_empty: function () {
|
||||
return !this.$input.text().trim() && !this.$('.o_attachments').children().length;
|
||||
return !this.$input.val().trim() && !this.$('.o_attachments').children().length;
|
||||
},
|
||||
focus: function () {
|
||||
this.$input.focus();
|
||||
@@ -901,7 +825,7 @@ var BasicComposer = Widget.extend({
|
||||
if (this.$emojisContainer.parent().length) {
|
||||
this._hideEmojis();
|
||||
} else {
|
||||
this.$emojisContainer.appendTo(this.$('.o_composer_container'));
|
||||
this.$emojisContainer.appendTo(this.$('.o_composer'));
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -925,31 +849,6 @@ var BasicComposer = Widget.extend({
|
||||
_onEmojiImageFocus: function () {
|
||||
clearTimeout(this._hideEmojisTimeout);
|
||||
},
|
||||
/**
|
||||
* set cursor position at the end in input box
|
||||
*
|
||||
* @todo move that kind of code in a utility file. Maybe dom.js
|
||||
*
|
||||
* @private
|
||||
* @param {Element} el
|
||||
*/
|
||||
_placeCaretAtEnd: function (el) {
|
||||
el.focus();
|
||||
if (typeof window.getSelection !== "undefined"
|
||||
&& typeof document.createRange !== "undefined") {
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(el);
|
||||
range.collapse(false);
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
} else if (typeof document.body.createTextRange !== "undefined") {
|
||||
var textRange = document.body.createTextRange();
|
||||
textRange.moveToElementText(el);
|
||||
textRange.collapse(false);
|
||||
textRange.select();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Called when an emoji is clicked -> adds it in the <input/>, focuses the
|
||||
* <input/> and closes the emoji panel.
|
||||
@@ -958,8 +857,8 @@ var BasicComposer = Widget.extend({
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onEmojiImageClick: function (ev) {
|
||||
this.$input.html(this.$input.html() + " " + $(ev.currentTarget).data('emoji') + " ");
|
||||
this._placeCaretAtEnd(this.$input[0]);
|
||||
this.$input.val(this.$input.val() + " " + $(ev.currentTarget).data('emoji') + " ");
|
||||
this.$input.focus();
|
||||
this._hideEmojis();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
position: relative;
|
||||
background: white;
|
||||
|
||||
input {
|
||||
textarea, input {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
@@ -27,19 +27,9 @@
|
||||
}
|
||||
|
||||
.o_composer {
|
||||
.o_composer_input[contenteditable=true]:empty:before {
|
||||
content: attr(placeholder);
|
||||
opacity: 0.4;
|
||||
}
|
||||
.o_composer_input {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
cursor: text;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
padding: 0px 0px @o-mail-chatter-gap;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
.o_composer_input > textarea {
|
||||
width: 100%;
|
||||
resize: none;
|
||||
}
|
||||
.o_composer_mention_dropdown .o_mention_proposition > a {
|
||||
.o-text-overflow();
|
||||
@@ -68,16 +58,17 @@
|
||||
color: @odoo-brand-optional;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_mail_emoji_container {
|
||||
padding: 0 @o-mail-chatter-gap @o-mail-chatter-gap 0;
|
||||
border: 1px solid @gray-lighter-dark;
|
||||
background-color: @gray-lighter;
|
||||
.o_mail_emoji_container {
|
||||
padding: 0 @o-mail-chatter-gap @o-mail-chatter-gap 0;
|
||||
border: 1px solid @gray-lighter-dark;
|
||||
border-width: 1px 0 0 0;
|
||||
background-color: @gray-lighter;
|
||||
|
||||
.o_mail_emoji {
|
||||
margin: @o-mail-chatter-gap 0 0 @o-mail-chatter-gap;
|
||||
cursor: pointer; // only in the popover
|
||||
.o_mail_emoji {
|
||||
margin: @o-mail-chatter-gap 0 0 @o-mail-chatter-gap;
|
||||
cursor: pointer; // only in the popover
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,13 +94,19 @@
|
||||
|
||||
.o_composer {
|
||||
border-radius: 2px;
|
||||
textarea {
|
||||
padding-right: @grid-gutter-width; // avoid overlaps betwenn textarea and o_composer_button_full_composer
|
||||
}
|
||||
|
||||
.o_mail_emoji_container {
|
||||
margin: 0 -@o-mail-chatter-gap;
|
||||
}
|
||||
}
|
||||
|
||||
.o_composer, .o_composer_subject {
|
||||
border: 1px solid @gray-lighter-dark;
|
||||
|
||||
input {
|
||||
textarea, input {
|
||||
padding: 0 0 @o-mail-chatter-gap;
|
||||
border: none;
|
||||
}
|
||||
@@ -121,9 +118,6 @@
|
||||
|
||||
.o_chatter_composer_tools {
|
||||
border-top: 1px solid @gray-lighter-dark;
|
||||
@media (max-width: @screen-xs-max) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.o_chat_inline_composer) {
|
||||
@@ -142,24 +136,26 @@
|
||||
|
||||
&.o_chat_inline_composer {
|
||||
.o_composer_container {
|
||||
.o-flex(1, 1, auto);
|
||||
.o-flex(1, 0, auto);
|
||||
}
|
||||
.o_composer {
|
||||
display: flex;
|
||||
min-height: 40px;
|
||||
padding: @o-mail-chatter-gap @o-mail-chatter-gap 0 @o-mail-chatter-gap;
|
||||
border-radius: 2px 0 0 2px;
|
||||
border-right-width: 0;
|
||||
}
|
||||
.o_mail_emoji_container {
|
||||
text-align: right;
|
||||
border-width: 1px;
|
||||
|
||||
.o_mail_emoji_container {
|
||||
text-align: right;
|
||||
border-right-width: 1px;
|
||||
}
|
||||
}
|
||||
.o_composer_input {
|
||||
.o-flex-display();
|
||||
.o-flex-flow(row, wrap);
|
||||
}
|
||||
.o_composer_text_field {
|
||||
.o-flex(1, 1, 50%);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.o_chatter_composer_tools {
|
||||
.o-flex(0, 1, auto);
|
||||
@@ -191,25 +187,32 @@
|
||||
border-top: 1px solid @gray-lighter-dark;
|
||||
width: 100%;
|
||||
}
|
||||
.o_mail_emoji_container {
|
||||
.o-position-absolute(auto, 0, 100%, 0);
|
||||
border-bottom-width: 1px;
|
||||
.o_composer {
|
||||
.o_mail_emoji_container {
|
||||
.o-position-absolute(auto, 0, 100%, 0);
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
}
|
||||
.o_composer_input {
|
||||
.o-flex-display();
|
||||
.o-flex-flow(row, nowrap);
|
||||
|
||||
textarea.o_composer_text_field {
|
||||
.o-flex(1, 1, auto);
|
||||
}
|
||||
|
||||
.o_chatter_composer_tools {
|
||||
.o-flex(0, 0, auto);
|
||||
}
|
||||
@media (max-width: @screen-xs-max) {
|
||||
.o_composer_button_send {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.o_composer_button_send, .o_chatter_avatar {
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: @screen-xs-max) {
|
||||
.o_composer_button_send {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,14 +42,10 @@
|
||||
}
|
||||
.o_chatter_composer_tools {
|
||||
.o-position-absolute(@right: 0px, @top: 0px);
|
||||
|
||||
@media (max-width: @screen-xs-max) {
|
||||
.o-position-absolute(@right: 6px, @top: 0px);
|
||||
}
|
||||
button {
|
||||
padding: 8px;
|
||||
padding: 10px;
|
||||
@media (max-width: @screen-xs-max) {
|
||||
padding: 7px 6px 0px 6px;
|
||||
padding: 13px;
|
||||
}
|
||||
&.btn-icon {
|
||||
font-size: 1.3em;
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
<input tabindex="1" type="text" placeholder="Subject" class="o_input"/>
|
||||
</div>
|
||||
<div t-attf-class="o_composer #{widget.extended ? 'o_extended_composer' : ''}">
|
||||
<div class="o_composer_input o_composer_text_field form-control" contenteditable="true" placeholder="Write Something..."/>
|
||||
<div class="o_chatter_composer_tools">
|
||||
<button tabindex="4" class="btn btn-sm btn-icon fa fa-smile-o o_composer_button_emoji" type="button" data-toggle="popover"/>
|
||||
<button tabindex="5" class="btn btn-sm btn-icon fa fa-paperclip o_composer_button_add_attachment" type="button"/>
|
||||
<button t-if="widget.options.isMobile" tabindex="3" class="btn btn-sm btn-icon fa fa-paper-plane-o o_composer_button_send" type="button"/>
|
||||
<div class="o_composer_input">
|
||||
<textarea class="o_input o_composer_text_field" tabindex="2" placeholder="Write something..."/>
|
||||
<div class="o_chatter_composer_tools">
|
||||
<button tabindex="4" class="btn btn-sm btn-icon fa fa-smile-o o_composer_button_emoji" type="button" data-toggle="popover"/>
|
||||
<button tabindex="5" class="btn btn-sm btn-icon fa fa-paperclip o_composer_button_add_attachment" type="button"/>
|
||||
<button t-if="widget.options.isMobile" tabindex="3" class="btn btn-sm btn-icon fa fa-paper-plane-o o_composer_button_send" type="button"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_composer_attachments_list"/>
|
||||
|
||||
@@ -368,9 +368,7 @@ QUnit.test('kanban activity widget with an activity', function (assert) {
|
||||
|
||||
QUnit.test('chatter: post, receive and star messages', function (assert) {
|
||||
var done = assert.async();
|
||||
assert.expect(28);
|
||||
|
||||
var unpatchWindowGetSelection = testUtils.patchWindowGetSelection();
|
||||
assert.expect(27);
|
||||
|
||||
var bus = new Bus();
|
||||
var BusService = createBusService(bus);
|
||||
@@ -473,7 +471,7 @@ QUnit.test('chatter: post, receive and star messages', function (assert) {
|
||||
// send a message
|
||||
form.$('.o_chatter_button_new_message').click();
|
||||
assert.ok(!$('.oe_chatter .o_chat_composer').hasClass('o_hidden'), "chatter should be opened");
|
||||
form.$('.oe_chatter .o_composer_text_field:first()').text("My first message");
|
||||
form.$('.oe_chatter .o_composer_text_field:first()').val("My first message");
|
||||
form.$('.oe_chatter .o_composer_button_send').click();
|
||||
assert.ok($('.oe_chatter .o_chat_composer').hasClass('o_hidden'), "chatter should be closed");
|
||||
assert.strictEqual(form.$('.o_thread_message').length, 2, "thread should contain two messages");
|
||||
@@ -487,7 +485,7 @@ QUnit.test('chatter: post, receive and star messages', function (assert) {
|
||||
// log a note
|
||||
form.$('.o_chatter_button_log_note').click();
|
||||
assert.ok(!$('.oe_chatter .o_chat_composer').hasClass('o_hidden'), "chatter should be opened");
|
||||
form.$('.oe_chatter .o_composer_text_field:first()').text("My first note");
|
||||
form.$('.oe_chatter .o_composer_text_field:first()').val("My first note");
|
||||
form.$('.oe_chatter .o_composer_button_send').click();
|
||||
assert.ok($('.oe_chatter .o_chat_composer').hasClass('o_hidden'), "chatter should be closed");
|
||||
assert.strictEqual(form.$('.o_thread_message').length, 3, "thread should contain three messages");
|
||||
@@ -513,7 +511,10 @@ QUnit.test('chatter: post, receive and star messages', function (assert) {
|
||||
// very basic test of mention
|
||||
form.$('.o_chatter_button_new_message').click();
|
||||
var $input = form.$('.oe_chatter .o_composer_text_field:first()');
|
||||
$input.text('@');
|
||||
$input.val('@');
|
||||
// the cursor position must be set for the mention manager to detect that we are mentionning
|
||||
$input[0].selectionStart = 1;
|
||||
$input[0].selectionEnd = 1;
|
||||
$input.trigger('keyup');
|
||||
|
||||
assert.strictEqual(getSuggestionsDef.state(), "pending",
|
||||
@@ -531,14 +532,10 @@ QUnit.test('chatter: post, receive and star messages', function (assert) {
|
||||
assert.strictEqual(form.$('.o_mention_proposition .o_mention_info').text(), '(test@odoo.com)',
|
||||
"suggestion should be displayed correctly");
|
||||
|
||||
$(".o_mention_proposition > a").trigger("click");
|
||||
assert.strictEqual($(".o_composer_input a").length, 1, "mention is 'green' in edit mode");
|
||||
|
||||
BasicComposer.prototype.MENTION_THROTTLE = mentionThrottle;
|
||||
|
||||
//cleanup
|
||||
form.destroy();
|
||||
unpatchWindowGetSelection();
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -597,7 +594,7 @@ QUnit.test('chatter: post a message and switch in edit mode', function (assert)
|
||||
|
||||
// send a message
|
||||
form.$('.o_chatter_button_new_message').click();
|
||||
form.$('.oe_chatter .o_composer_text_field:first()').text("My first message");
|
||||
form.$('.oe_chatter .o_composer_text_field:first()').val("My first message");
|
||||
form.$('.oe_chatter .o_composer_button_send').click();
|
||||
assert.strictEqual(form.$('.o_thread_message').length, 1, "thread should contain a message");
|
||||
assert.ok(form.$('.o_thread_message:first() .o_thread_message_core').text().indexOf('My first message') >= 0,
|
||||
|
||||
@@ -14,7 +14,6 @@ var testUtils = require('web.test_utils');
|
||||
var BasicComposer = Composers.BasicComposer;
|
||||
var createBusService = mailTestUtils.createBusService;
|
||||
var createDiscuss = mailTestUtils.createDiscuss;
|
||||
var patchWindowGetSelection = testUtils.patchWindowGetSelection;
|
||||
|
||||
QUnit.module('mail', {}, function () {
|
||||
|
||||
@@ -127,14 +126,13 @@ QUnit.test('@ mention in channel', function (assert) {
|
||||
|
||||
// click on general
|
||||
$general.click();
|
||||
var $input = discuss.$('.o_composer_input').first();
|
||||
var $input = discuss.$('textarea.o_composer_text_field').first();
|
||||
assert.ok($input.length, "should display a composer input");
|
||||
|
||||
// Simulate '@' typed by user with mocked Window.getSelection
|
||||
// Note: focus is needed in order to trigger rpc 'channel_fetch_listeners'
|
||||
$input.focus();
|
||||
$input.text("@");
|
||||
var unpatchWindowGetSelection = patchWindowGetSelection();
|
||||
$input.val("@");
|
||||
$input.trigger('keyup');
|
||||
|
||||
fetchListenersDef
|
||||
@@ -220,7 +218,7 @@ QUnit.test('@ mention in channel', function (assert) {
|
||||
$input.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER}));
|
||||
assert.strictEqual(discuss.$('.o_mention_proposition').length, 0,
|
||||
"should not have any partner mention proposition after ENTER");
|
||||
assert.strictEqual($input.find('a').text() , "@Admin",
|
||||
assert.strictEqual($input.val().trim() , "@Admin",
|
||||
"should have the correct mention link in the composer input");
|
||||
|
||||
// send message
|
||||
@@ -239,7 +237,6 @@ QUnit.test('@ mention in channel', function (assert) {
|
||||
// Restore throttles and window.getSelection
|
||||
BasicComposer.prototype.MENTION_THROTTLE = mentionThrottle;
|
||||
ChatManager.prototype.CHANNEL_SEEN_THROTTLE = channelThrottle;
|
||||
unpatchWindowGetSelection();
|
||||
discuss.destroy();
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -725,53 +725,6 @@ function patch (target, props) {
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* In Phantomjs, there is a crash when calling window.getSelection
|
||||
* in order for the tests to work, for the specific test that uses it, replace
|
||||
* the default window.getSelection by a mock
|
||||
*
|
||||
* usage:
|
||||
* QUnit.test('...',function(done){
|
||||
* var unpatchWindowGetSelection = testUtils.patchWindowGetSelection();
|
||||
*
|
||||
* // do something that needs to use window.getSelection()
|
||||
* assert.strictEqual(....);
|
||||
*
|
||||
* // restore the original function
|
||||
* unpatchWindowGetSelection();
|
||||
*
|
||||
* // finish the test
|
||||
* done();
|
||||
* })
|
||||
*
|
||||
* @returns {function} the unpatch function
|
||||
*/
|
||||
function patchWindowGetSelection() {
|
||||
var originalWindowGetSelection = window.getSelection;
|
||||
window.getSelection = function () {
|
||||
return {
|
||||
removeAllRanges: function () {},
|
||||
addRange: function (range) {},
|
||||
getRangeAt: function (index) {
|
||||
return {
|
||||
startOffset : 0
|
||||
};
|
||||
},
|
||||
anchorNode: {
|
||||
parentNode: {
|
||||
childNodes: [{
|
||||
outerHTML: "@",
|
||||
nodeType: 3,
|
||||
textContent: '@',
|
||||
}],
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
return function () {
|
||||
window.getSelection = originalWindowGetSelection;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpatches a given Class or Object.
|
||||
@@ -823,7 +776,6 @@ return $.when(
|
||||
intercept: intercept,
|
||||
observe: observe,
|
||||
patch: patch,
|
||||
patchWindowGetSelection: patchWindowGetSelection,
|
||||
removeSrcAttribute: removeSrcAttribute,
|
||||
triggerKeypressEvent: triggerKeypressEvent,
|
||||
triggerMouseEvent: triggerMouseEvent,
|
||||
|
||||
Reference in New Issue
Block a user